Compare commits

...

198 Commits

Author SHA1 Message Date
David Sehnal
d42c9a6e15 0.7.0-dev.2 2020-04-22 13:09:14 +02:00
David Sehnal
da4dabc3f5 fix package.json bin path 2020-04-22 13:08:17 +02:00
David Sehnal
92217905f8 0.7.0-dev.1 2020-04-22 12:27:57 +02:00
Alexander Rose
598441a727 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 18:18:01 -07:00
Alexander Rose
f707cb19a4 volume loci, picking and repr improvements
- Volume.Loci
- Volume.Isosurface.Loci
- Volume.Cell.Loci
- picking
- wip: slice
2020-04-21 18:17:51 -07:00
David Sehnal
63fc408be6 ajaxGet: support custom http headers 2020-04-21 23:36:56 +02:00
David Sehnal
69dedd8c22 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 22:51:22 +02:00
David Sehnal
5480805754 AssignColorVolume
+ UI bugfix
2020-04-21 22:50:49 +02:00
David Sehnal
655ae65b8d dxbin support 2020-04-21 21:59:27 +02:00
David Sehnal
1a23cb672e dx parser 2020-04-21 21:24:59 +02:00
Alexander Rose
19e18b4089 MC: ensure winding-order and normals dir are same for neg/pos iso-level 2020-04-21 11:36:41 -07:00
Alexander Rose
3d909d5012 fix, register Dsn6Provider as dsn6 2020-04-21 11:12:38 -07:00
David Sehnal
ad521948b6 VolumeData.transform 2020-04-21 20:10:22 +02:00
Alexander Rose
2f3b6a28c1 support older REMARK 350 format, #34 2020-04-21 10:42:05 -07:00
Alexander Rose
c779da674c Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 10:18:57 -07:00
Alexander Rose
d9b140f9f2 added Model.probablyHasDensityMap 2020-04-21 10:18:39 -07:00
David Sehnal
052648023e VolumeSourceControls
- initial version, needs more work
2020-04-21 19:07:08 +02:00
David Sehnal
f5d12d440e DownloadFile state action 2020-04-21 17:24:56 +02:00
David Sehnal
99d7a90863 Basic cube format support
- TODO: non-orthogonal frames
2020-04-21 17:07:22 +02:00
Alexander Rose
901d5c86e6 wip, stub for volume-slice representation 2020-04-20 19:09:39 -07:00
Alexander Rose
df9efd05e6 image geometry 2020-04-20 19:04:55 -07:00
David Sehnal
26b8adaec4 extensions/cellpack: use plugin.runTask instead of Task.run 2020-04-21 02:43:09 +02:00
David Sehnal
fc6f5a0336 Merge pull request #33 from corredD/forkdev
Forkdev
2020-04-21 02:34:04 +02:00
Alexander Rose
f823a887b7 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-20 10:25:26 -07:00
Alexander Rose
b346d4d85d tweaked selection/focus remove icon 2020-04-20 10:21:43 -07:00
Alexander Rose
70bd035898 shader refactoring
- add dRenderVariant
- add convenince defines
2020-04-20 10:21:04 -07:00
David Sehnal
7e5cdd8e06 mol-plugin-ui: improved selection controls 2020-04-20 18:55:28 +02:00
David Sehnal
a21dac60e0 Rename "full state" to Session
+ ProteopediaWrapper.snapshot tweaks
2020-04-20 18:11:38 +02:00
David Sehnal
9bd2e0d96e typo fix 2020-04-20 17:52:21 +02:00
autin
dd15a000e1 clean the console.log 2020-04-20 13:39:01 +02:00
autin
ebcfa44f22 OPM support 2020-04-20 13:19:21 +02:00
David Sehnal
43845adb71 import materialui icons separately
- avoids loading extra ~5k modules in webpack
2020-04-19 17:02:52 +02:00
David Sehnal
9e3fff65a7 eslint config fix 2020-04-19 16:08:20 +02:00
David Sehnal
05c35a3a3a mol-model: cif export copyAllCategories option
- support in model-server
- moved servers build config
- fixed swagger template not working with the separate server build
2020-04-19 16:01:47 +02:00
David Sehnal
6f46965344 model-server: change response header for query-many exceeded limit 2020-04-19 14:02:05 +02:00
Alexander Rose
27ee576340 improved .hasDensityMap and .isFromPdbArchive helpers 2020-04-18 13:39:44 -07:00
David Sehnal
58492328df max height for Screenshot / State Snapshot panel 2020-04-18 16:01:17 +02:00
David Sehnal
fd102bede1 mol-plugin-ui: close Screenshot/snapshot panel after saving/opening state 2020-04-18 15:54:50 +02:00
David Sehnal
3d8c47eefa proteopedia-wrapper: support zipped state loading 2020-04-18 15:51:25 +02:00
David Sehnal
c4e43228a2 add Component: option to check if an equivalent component already exists 2020-04-18 15:31:51 +02:00
David Sehnal
6526090b8b mol-plugin-ui: added "add representation" to selection mode 2020-04-18 15:03:49 +02:00
David Sehnal
094a018b5b mol-plugin: global state save options 2020-04-18 14:28:58 +02:00
Alexander Rose
a0a9c994b2 basic asset support for volume streaming 2020-04-17 20:37:14 -07:00
Alexander Rose
524ed90e3f lru cache set returns removed entry 2020-04-17 20:36:20 -07:00
Alexander Rose
7abba750b4 0.7.0-dev.0 2020-04-17 18:26:41 -07:00
Alexander Rose
ecff1641d2 package updates 2020-04-17 18:21:02 -07:00
Alexander Rose
47ecf04386 add version field to state snapshot 2020-04-17 18:20:51 -07:00
Alexander Rose
c4697a9f45 use mean not boundary center for transform in cellpack 2020-04-17 17:47:41 -07:00
Alexander Rose
53d5414492 cellpack improvements
- support asset-manager
- allow bcif as ingredient files
- load pdb entries as bcif
2020-04-17 17:29:06 -07:00
Alexander Rose
311f5c09f5 use Asset.File[] for PD.FileList 2020-04-17 17:27:14 -07:00
Alexander Rose
be4439451f Merge branch 'master' of https://github.com/molstar/molstar 2020-04-17 11:52:23 -07:00
Alexander Rose
09c83e83ba improved Model.isFromPdbArchive 2020-04-17 11:49:23 -07:00
Alexander Rose
bea4c64d85 use asset-manager for custom-properties
- obtain function needs to return a value and assets
- assets are stored per descriptor in model/structure
- assets shold be released via customProperties.dispose()
2020-04-17 11:42:59 -07:00
David Sehnal
0387dcd444 proteopedia-wrapper: focusFirst label options 2020-04-17 20:42:44 +02:00
Alexander Rose
a89f21dafd Merge branch 'master' of https://github.com/molstar/molstar 2020-04-17 11:24:06 -07:00
Alexander Rose
7921c05d55 use .molx and .molj for file input 2020-04-17 11:08:37 -07:00
Alexander Rose
dafa946937 Asset.getUrlAsset support body 2020-04-17 11:07:50 -07:00
David Sehnal
95673b0131 do not build browserTests in production build, added build-viewer task
- reduced the production build time by several minutes
  (it took 30+s for each "subproject" and webpack was building them linearly)
2020-04-17 19:10:05 +02:00
David Sehnal
0118136869 fix build error with latest express type annotations 2020-04-17 18:55:21 +02:00
David Sehnal
573c2a7ad6 proteopedia-wrapper: support bcif 2020-04-17 18:37:01 +02:00
David Sehnal
41977ea758 DownloadStructure: support static updated bcif from PDBe 2020-04-17 17:30:01 +02:00
David Sehnal
75ccded612 support plain string in ParamDefinition.UrlParam 2020-04-17 17:08:29 +02:00
David Sehnal
2429111a59 state file export improvements
- save full state including all snapshots instead of just the current one
2020-04-17 15:39:13 +02:00
David Sehnal
c475cb292e support drag & drop for state files 2020-04-17 12:58:39 +02:00
David Sehnal
04d34b369a add snapshot download UI to "screenshot" panel 2020-04-17 12:50:21 +02:00
David Sehnal
cfe4c6c559 mol-plugin: moved state related events to PluginState 2020-04-17 12:36:22 +02:00
David Sehnal
4d13f99d22 mol-plugin: refactoring managers 2020-04-17 12:30:34 +02:00
David Sehnal
62ebd4d8a9 mol-plugin-state: PluginStateSnapshotManager refactor 2020-04-17 12:14:55 +02:00
David Sehnal
026c25621f add missing imports 2020-04-17 00:08:20 +02:00
David Sehnal
c096ae299d Merge branch 'master' of https://github.com/molstar/molstar 2020-04-17 00:06:54 +02:00
David Sehnal
ae306d1761 data asset handling improvements 2020-04-17 00:06:41 +02:00
Alexander Rose
4caae32933 add missing type 2020-04-16 13:58:15 -07:00
Alexander Rose
6bbf20980e Merge branch 'master' of https://github.com/molstar/molstar 2020-04-16 13:54:46 -07:00
David Sehnal
0fd00ecab8 mol-plugin-ui: support AssetManager in DownloadBlob 2020-04-16 22:33:21 +02:00
David Sehnal
70de73a95b mol-state: call StateTransformer.dispose when recreating object 2020-04-16 21:58:42 +02:00
Alexander Rose
578ad69c36 add current structure-focus to state snapshots 2020-04-16 11:01:07 -07:00
David Sehnal
af263b8c88 removed unused code 2020-04-16 17:09:03 +02:00
David Sehnal
bef6775de5 refactored AssetManager
- support assets in OpenFile and Download transforms
2020-04-16 17:04:21 +02:00
Alexander Rose
3c27450e82 zipped snapshot that include files from asset-manager 2020-04-15 18:29:43 -07:00
Alexander Rose
9400f27f82 basic asset manager for file objects 2020-04-15 18:28:47 -07:00
Alexander Rose
c176313f7b added zip data type that returns object of Uint8Arrays 2020-04-15 18:27:39 -07:00
Alexander Rose
77461d5a23 fix focus control breaks for non-atomic units 2020-04-15 16:00:53 -07:00
Alexander Rose
a5c039bf11 fix Si-Si bonds 2020-04-15 13:11:23 -07:00
Alexander Rose
e3c08f23bf avoid adding many identical single atom units in builder 2020-04-15 12:22:41 -07:00
Alexander Rose
67a26e93e4 occupancy handling improvements
- add occ of 1 to cif-core if not defined
- check if occ is defined before disallowing bonds
2020-04-15 12:15:26 -07:00
David Sehnal
a1d261b7c8 ui tweaks 2020-04-15 17:32:24 +02:00
David Sehnal
3826394940 Support SVG icon for CollapsableControls & fixed logo image 2020-04-15 17:13:43 +02:00
David Sehnal
791a54aeec ui tweaks 2020-04-15 13:46:20 +02:00
David Sehnal
48f1bb7755 icon tweaks 2020-04-15 11:56:42 +02:00
Alexander Rose
53e028325f updated packages, removed font building code 2020-04-14 17:09:00 -07:00
David Sehnal
02eda0a1e2 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-15 01:55:32 +02:00
David Sehnal
92600160ff lint config fix 2020-04-15 01:54:27 +02:00
Alexander Rose
6ec67a7faf Merge branch 'master' of https://github.com/molstar/molstar 2020-04-14 16:52:55 -07:00
Alexander Rose
a4386744a2 fix Model.hasCrystalSymmetry test 2020-04-14 16:52:09 -07:00
David Sehnal
09210279e1 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-15 01:42:35 +02:00
David Sehnal
e78cf18a38 separate build for servers
- because of problems with ES6 modules (might revert later)
+ css tweaks
2020-04-15 01:40:52 +02:00
Alexander Rose
9d1d7bbf72 moved pdbe code to extensions folder 2020-04-14 16:35:41 -07:00
Alexander Rose
60d5e85b4c moved rcsb code to extensions folder 2020-04-14 16:35:34 -07:00
David Sehnal
2c263e216f Merge branch 'master' of https://github.com/molstar/molstar 2020-04-15 01:21:04 +02:00
David Sehnal
e1f671a5b3 use material ui icons instead of fontello, do not import google font 2020-04-15 01:20:14 +02:00
Alexander Rose
7ffaea48cb read cif-core bfactor field 2020-04-14 15:39:34 -07:00
Alexander Rose
0e4527613c handle charge in cif-core type_symbol field #31 2020-04-14 15:36:20 -07:00
Alexander Rose
f363621de3 moved cellpack to toplevel extensions dir and register as behavior 2020-04-14 12:24:03 -07:00
Alexander Rose
4901ec3b5d added/removed events for StateActionManager 2020-04-14 12:23:55 -07:00
David Sehnal
c592b8a53f fix Mat4Control in Firefox, track Transform in StructureHierarchy, fix findUpdateRoots when there is error 2020-04-14 19:34:59 +02:00
David Sehnal
325e4e44dc Structure.transform parent revert 2020-04-14 15:22:49 +02:00
David Sehnal
a81f7f6cfd Merge branch 'master' of https://github.com/molstar/molstar 2020-04-14 15:21:08 +02:00
David Sehnal
31b83e9675 StructureSourceControls: added 'conformation transform' section 2020-04-14 15:20:11 +02:00
David Sehnal
dc89f33cd0 FocusLoci fix
- was calling camera.focusLoci twice
- broken for transformed structures
2020-04-14 15:05:58 +02:00
David Sehnal
f5f9237a78 apply Structure.coordinateSystem to interactions visual 2020-04-14 14:56:15 +02:00
David Sehnal
4429ff0ac1 Coordinate system changes- remove StructureCoordinateSystem transform * TransformStructureConformation should be enough- update Structure.coordinateSystem- update Structure.transform to keep parent's parent 2020-04-14 14:27:09 +02:00
David Sehnal
a0d38e6b10 Coordinate system changes
- remove StructureCoordinateSystem transform
  * TransformStructureConformation should be enough
- update Structure.coordinateSystem
- update Structure.transform to keep parent's parent
2020-04-14 14:13:16 +02:00
David Sehnal
c060664f84 SubstructureParentHelper.get takes decorators into account 2020-04-14 13:57:25 +02:00
David Sehnal
10f7f15f70 mol-plugin-ui: Mat4 UI & removed NumericInput in favor of TextInput.numeric 2020-04-14 13:16:31 +02:00
Alexander Rose
d40ff29337 0.6.8 2020-04-13 19:45:10 -07:00
Alexander Rose
7ca666ae98 fix wrong TransformStructureConformation params 2020-04-13 19:43:12 -07:00
Alexander Rose
dcaea9d40b tweaked focus repr params
- bigger than standard params
2020-04-13 17:09:04 -07:00
Alexander Rose
b878273d81 some basic bird molecule support 2020-04-13 15:26:39 -07:00
Alexander Rose
c15d29f2da linting 2020-04-13 13:06:32 -07:00
Alexander Rose
3e3a13070a Merge branch 'master' of https://github.com/molstar/molstar 2020-04-13 13:03:42 -07:00
Alexander Rose
11bcd84e66 optionally transpose mat4 in structure transformation transforms 2020-04-13 13:03:06 -07:00
Alexander Rose
44b63e5953 json imput for Mat4 paraemter ui 2020-04-13 13:01:05 -07:00
David Sehnal
fb0634a0f4 added semicolons to linting rules 2020-04-13 21:17:39 +02:00
David Sehnal
0b651db35b mol-plugin-ui: cache focus options in StructureFocusControls 2020-04-13 13:42:52 +02:00
David Sehnal
b24062b575 tweak proteopedia-wrapper.ModelInfo 2020-04-13 13:00:40 +02:00
Alexander Rose
35c5c4cfc0 linting improvements 2020-04-11 13:15:26 -07:00
Alexander Rose
4b8bf57d1a simplified cellpack loadMembrane 2020-04-11 12:56:30 -07:00
Alexander Rose
a81f0d911a linting style 2020-04-11 12:37:06 -07:00
Alexander Rose
28e2227989 Merge pull request #30 from corredD/forkdev
Forkdev
2020-04-11 12:13:44 -07:00
DESKTOP-O6LIMN9\ludov
4f97b6836a eslint formating 2020-04-11 14:31:31 +02:00
DESKTOP-O6LIMN9\ludov
5ab0dfaba8 formatting 2020-04-11 14:24:11 +02:00
DESKTOP-O6LIMN9\ludov
66751fa006 eslint format 2020-04-11 14:19:07 +02:00
DESKTOP-O6LIMN9\ludov
f1d78e4805 support for membrane in list files and on the server if available as bcif
clean the console.log
2020-04-11 14:13:43 +02:00
DESKTOP-O6LIMN9\ludov
715c78a04f Merge branch 'master' into forkdev 2020-04-11 11:27:41 +02:00
DESKTOP-O6LIMN9\ludov
0cd8b4ee8c Merge branch 'master' of https://github.com/molstar/molstar 2020-04-11 11:26:57 +02:00
Alexander Rose
c6efa475a5 better cif-core data naming 2020-04-10 19:47:54 -07:00
Alexander Rose
e846e1fdd7 fixed EllipsoidParams.linkCap param 2020-04-10 19:32:22 -07:00
Alexander Rose
4221067f8f added AtomSiteAnisotrop support for cifCore 2020-04-10 19:27:52 -07:00
Alexander Rose
36951d6f19 make AtomSiteAnisotrop less dependent on mmCIF 2020-04-10 19:27:24 -07:00
Alexander Rose
ac45500e35 added FormatRegistry.isApplicable and properly check AtomSiteAnisotrop 2020-04-10 19:26:22 -07:00
Alexander Rose
0f22eab8b9 support matrix type for cif-core schema 2020-04-10 19:25:05 -07:00
Alexander Rose
5a8fd6d518 beter symmetry handling in index-pair bonds 2020-04-10 15:07:18 -07:00
Alexander Rose
60ee04b9b9 linting improvements
- check tsx as well
- check brace-style
- error more instead of warn
2020-04-10 12:18:07 -07:00
David Sehnal
d99b5bd505 mol-state: throw error when trying to reuse StateBuilder 2020-04-10 15:47:40 +02:00
David Sehnal
6312c1f99b use StateBuilder.commit() instead of PluginContext.updateDataState() 2020-04-10 15:42:28 +02:00
David Sehnal
ef870a510f fix empty strings loci labels 2020-04-10 15:10:06 +02:00
David Sehnal
394ff05626 refactored format registry & added StateBuilder.commit() 2020-04-10 14:55:50 +02:00
Alexander Rose
e90ccfdd20 basic support for models from cif-core files 2020-04-09 18:42:33 -07:00
Alexander Rose
92a86e324b more general Model.hasCrystalSymmetry
- not just limited to mmCIF
2020-04-09 18:12:46 -07:00
Alexander Rose
e5d6816392 allow for improper rotations
- in Mat4.isRotationAndTranslation
2020-04-09 17:55:14 -07:00
Alexander Rose
a1b2de16a3 removed console.log 2020-04-09 17:41:17 -07:00
Alexander Rose
f605021cfb tweaked naming 2020-04-09 17:33:55 -07:00
Alexander Rose
619a2ccb3a use MOL (not UNK) as name for unknown ligand molecules
- UNK is for unknown amino acids in CCD
2020-04-09 17:33:16 -07:00
Alexander Rose
0cc077c346 more cif-core fields 2020-04-09 17:31:17 -07:00
Alexander Rose
e34aad991b fixed dics build path of cifschema app 2020-04-09 17:30:49 -07:00
Alexander Rose
2a925cdd6a tweaked bond calc
- use avg element threshold
2020-04-09 17:30:04 -07:00
Alexander Rose
36c5e444ca apply options when adding new structure component
- use StructureComponentManager.state.options
2020-04-09 14:37:54 -07:00
Alexander Rose
c7f6041aa4 handle empty File or FileList params 2020-04-09 10:11:31 -07:00
DESKTOP-O6LIMN9\ludov
68401b3556 Merge branch 'master' of github.com:corredD/molstar-proto into forkdev 2020-04-09 09:45:11 +02:00
DESKTOP-O6LIMN9\ludov
3843064eac Merge branch 'master' of https://github.com/molstar/molstar
# Conflicts:
#	.github/workflows/lint.yml
2020-04-09 09:08:46 +02:00
ludovic autin
d0de9073e7 Create lint.yml 2020-04-09 09:04:11 +02:00
DESKTOP-O6LIMN9\ludov
35bd0ec5d8 Merge branch 'master' of https://github.com/molstar/molstar into forkdev
# Conflicts:
#	src/apps/viewer/extensions/cellpack/model.ts
2020-04-09 08:55:29 +02:00
Alexander Rose
373a69abbd cellpack, add support for local ingredient files 2020-04-08 22:40:35 -07:00
Alexander Rose
9fa56bf76c readded citschema and cif2bcif apps back from ciftools package
- easier to maintain
2020-04-08 20:08:35 -07:00
David Sehnal
3ad42fc7e0 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-09 03:18:28 +02:00
David Sehnal
7dd6efafb2 fixed some cyclical references & drawcall in mol-canvas3d 2020-04-09 03:18:21 +02:00
Alexander Rose
61e26df637 try different lint workflow 2020-04-08 18:02:47 -07:00
Alexander Rose
a47283bf5b change lint gh action path 2020-04-08 17:48:57 -07:00
Alexander Rose
d7e5385b04 add lint github action 2020-04-08 17:41:25 -07:00
Alexander Rose
8eaac83238 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-08 17:26:37 -07:00
Alexander Rose
9fbe224f00 reset color of all components when setting assembly-symmetry off 2020-04-08 17:24:09 -07:00
Alexander Rose
26dc7b2f65 0.6.7 2020-04-08 16:36:37 -07:00
Alexander Rose
bb6b6f9690 updated data.rcsb.org url and schema 2020-04-08 16:23:13 -07:00
Alexander Rose
0fabfc9873 0.6.6 2020-04-08 16:01:39 -07:00
Alexander Rose
9c3e057410 fix water-water hbond detection and set to off by default 2020-04-08 14:58:16 -07:00
Alexander Rose
f4cbb1ec73 ignore mouse moves when no button is held in SelectionViewportControls 2020-04-08 13:45:35 -07:00
Alexander Rose
1175f589eb slightly increased nci link size 2020-04-08 13:39:13 -07:00
Alexander Rose
8c7563d7f2 allow MET.SD as metal binding partner 2020-04-08 13:37:55 -07:00
Alexander Rose
33533cbb09 only color by assembly-symmetry-cluster if repr was added 2020-04-08 11:19:23 -07:00
Alexander Rose
0ec37a7b95 0.6.5 2020-04-08 09:48:05 -07:00
Alexander Rose
f3db0c171b added optional priority to LociLabelProvider 2020-04-08 09:47:15 -07:00
David Sehnal
cd10d23371 updated basic-wrapper, moved some apps to examples
+ fixed lighting demo
2020-04-08 17:18:57 +02:00
DESKTOP-O6LIMN9\ludov
d60c88c506 parsing the selection string 2020-04-08 14:53:36 +02:00
DESKTOP-O6LIMN9\ludov
7266aab4be support for latest cellpack model.
Example is ExosomeModel.json present in the repo
2020-04-08 14:31:52 +02:00
Alexander Rose
54c2ef7a0b better isApplicable checks for random-coil and density-fit colorings 2020-04-07 19:59:57 -07:00
Alexander Rose
b882a72d77 refactored assembly-symmetry behavior
- use tag and applyOrUpdate
- set default color theme when off
2020-04-07 19:55:15 -07:00
Alexander Rose
1eab4dac96 0.6.4 2020-04-07 16:59:43 -07:00
Alexander Rose
b2376aea70 mol format tweaks 2020-04-07 16:56:04 -07:00
Alexander Rose
1641cfbd03 improved assembly symmetry init and cage size 2020-04-07 16:34:30 -07:00
Alexander Rose
f27540ca02 fix assembly-symmetry init issues
- ensure that the default is a non C1 symmetry
2020-04-07 12:17:42 -07:00
David Sehnal
b4320de291 mol-plugin-state: added PubChem as DownloadStructure source 2020-04-07 17:03:13 +02:00
David Sehnal
1b26aa4b36 basic MOL/SDF support 2020-04-07 16:51:59 +02:00
Alexander Rose
1ca91f35e2 wrap all sequence residues in zero-width chars for proper line breaks 2020-04-06 18:35:22 -07:00
Alexander Rose
1c5ac56ecf 0.6.3 2020-04-06 17:59:02 -07:00
Alexander Rose
ee85e13206 fix assembly symmetry cage orientation calc 2020-04-06 17:57:09 -07:00
Alexander Rose
440474d53b focus improvements- support shift to add in focus menu- keep section in focus menu open- always include loci label 2020-04-06 17:20:02 -07:00
Alexander Rose
d9c251b2ce action menu improvements
- pass MouseEvent to OnSelect
- try to keep previously expanded sections
2020-04-06 16:17:38 -07:00
Alexander Rose
975b4aee2a show error when no entry for streaming is found 2020-04-06 14:37:03 -07:00
989 changed files with 33417 additions and 29870 deletions

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules/*
build/*
lib/*

View File

@@ -5,7 +5,7 @@
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"project": ["tsconfig.json", "tsconfig.servers.json"],
"sourceType": "module"
},
"plugins": [
@@ -14,8 +14,9 @@
"rules": {
"@typescript-eslint/ban-types": "warn",
"@typescript-eslint/class-name-casing": "off",
"indent": "off",
"@typescript-eslint/indent": [
"warn",
"error",
4
],
"@typescript-eslint/member-delimiter-style": [
@@ -33,7 +34,7 @@
],
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/quotes": [
"warn",
"error",
"single",
{
"avoidEscape": true,
@@ -44,22 +45,31 @@
"off",
null
],
"@typescript-eslint/type-annotation-spacing": "warn",
"@typescript-eslint/type-annotation-spacing": "error",
"arrow-parens": [
"off",
"as-needed"
],
"brace-style": "off",
"@typescript-eslint/brace-style": [
"error",
"1tbs", { "allowSingleLine": true }
],
"comma-spacing": "off",
"@typescript-eslint/comma-spacing": "error",
"space-infix-ops": "error",
"comma-dangle": "off",
"eqeqeq": [
"warn",
"error",
"smart"
],
"import/order": "off",
"no-eval": "warn",
"no-new-wrappers": "warn",
"no-trailing-spaces": "warn",
"no-trailing-spaces": "error",
"no-unsafe-finally": "warn",
"no-var": "warn",
"spaced-comment": "warn"
"no-var": "error",
"spaced-comment": "error",
"semi": "warn"
}
}

18
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
on:
push:
pull_request:
jobs:
eslint:
name: eslint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: install node v12
uses: actions/setup-node@v1
with:
node-version: 12
- name: yarn install
run: yarn install
- name: eslint
uses: icrawl/action-eslint@v1

View File

@@ -89,21 +89,20 @@ and navigate to `build/viewer`
### Code generation
**CIF schemas**
Install CIFTools `npm install ciftools -g`
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
**GraphQL schemas**
./node_modules/.bin/graphql-codegen -c ./data/rcsb-graphql/codegen.yml
./node_modules/.bin/graphql-codegen -c ./src/extensions/rcsb/graphql/codegen.yml
### Other scripts
**Create chem comp bond table**
export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
**Test model server**
@@ -115,9 +114,13 @@ Install CIFTools `npm install ciftools -g`
**Convert any CIF to BinaryCIF**
node build/model-server/preprocess -i file.cif -ob file.bcif
node lib/servers/model/preprocess -i file.cif -ob file.bcif
To see all available commands, use ``node build/model-server/preprocess -h``.
To see all available commands, use ``node lib/servers/model/preprocess -h``.
Or
node ./lib/apps/cif2bcif
## Development

View File

@@ -0,0 +1,88 @@
pdbx_reference_molecule.prd_id
pdbx_reference_molecule.name
pdbx_reference_molecule.represent_as
pdbx_reference_molecule.type
pdbx_reference_molecule.type_evidence_code
pdbx_reference_molecule.class
pdbx_reference_molecule.class_evidence_code
pdbx_reference_molecule.formula
pdbx_reference_molecule.chem_comp_id
pdbx_reference_molecule.formula_weight
pdbx_reference_molecule.release_status
pdbx_reference_molecule.replaces
pdbx_reference_molecule.replaced_by
pdbx_reference_molecule.compound_details
pdbx_reference_molecule.description
pdbx_reference_molecule.representative_PDB_id_code
pdbx_reference_entity_list.prd_id
pdbx_reference_entity_list.ref_entity_id
pdbx_reference_entity_list.component_id
pdbx_reference_entity_list.type
pdbx_reference_entity_list.details
pdbx_reference_entity_nonpoly.prd_id
pdbx_reference_entity_nonpoly.ref_entity_id
pdbx_reference_entity_nonpoly.name
pdbx_reference_entity_nonpoly.chem_comp_id
pdbx_reference_entity_link.prd_id
pdbx_reference_entity_link.link_id
pdbx_reference_entity_link.link_class
pdbx_reference_entity_link.ref_entity_id_1
pdbx_reference_entity_link.entity_seq_num_1
pdbx_reference_entity_link.comp_id_1
pdbx_reference_entity_link.atom_id_1
pdbx_reference_entity_link.ref_entity_id_2
pdbx_reference_entity_link.entity_seq_num_2
pdbx_reference_entity_link.comp_id_2
pdbx_reference_entity_link.atom_id_2
pdbx_reference_entity_link.value_order
pdbx_reference_entity_link.component_1
pdbx_reference_entity_link.component_2
pdbx_reference_entity_link.details
pdbx_reference_entity_poly_link.prd_id
pdbx_reference_entity_poly_link.ref_entity_id
pdbx_reference_entity_poly_link.link_id
pdbx_reference_entity_poly_link.atom_id_1
pdbx_reference_entity_poly_link.comp_id_1
pdbx_reference_entity_poly_link.entity_seq_num_1
pdbx_reference_entity_poly_link.atom_id_2
pdbx_reference_entity_poly_link.comp_id_2
pdbx_reference_entity_poly_link.entity_seq_num_2
pdbx_reference_entity_poly_link.value_order
pdbx_reference_entity_poly_link.component_id
pdbx_reference_entity_poly.prd_id
pdbx_reference_entity_poly.ref_entity_id
pdbx_reference_entity_poly.db_code
pdbx_reference_entity_poly.db_name
pdbx_reference_entity_poly.type
pdbx_reference_entity_sequence.prd_id
pdbx_reference_entity_sequence.ref_entity_id
pdbx_reference_entity_sequence.type
pdbx_reference_entity_sequence.NRP_flag
pdbx_reference_entity_sequence.one_letter_codes
pdbx_reference_entity_poly_seq.prd_id
pdbx_reference_entity_poly_seq.ref_entity_id
pdbx_reference_entity_poly_seq.num
pdbx_reference_entity_poly_seq.mon_id
pdbx_reference_entity_poly_seq.parent_mon_id
pdbx_reference_entity_poly_seq.hetero
pdbx_reference_entity_poly_seq.observed
pdbx_reference_entity_src_nat.prd_id
pdbx_reference_entity_src_nat.ref_entity_id
pdbx_reference_entity_src_nat.ordinal
pdbx_reference_entity_src_nat.taxid
pdbx_reference_entity_src_nat.organism_scientific
pdbx_reference_entity_src_nat.db_code
pdbx_reference_entity_src_nat.db_name
pdbx_prd_audit.prd_id
pdbx_prd_audit.date
pdbx_prd_audit.processing_site
pdbx_prd_audit.action_type
1 pdbx_reference_molecule.prd_id
2 pdbx_reference_molecule.name
3 pdbx_reference_molecule.represent_as
4 pdbx_reference_molecule.type
5 pdbx_reference_molecule.type_evidence_code
6 pdbx_reference_molecule.class
7 pdbx_reference_molecule.class_evidence_code
8 pdbx_reference_molecule.formula
9 pdbx_reference_molecule.chem_comp_id
10 pdbx_reference_molecule.formula_weight
11 pdbx_reference_molecule.release_status
12 pdbx_reference_molecule.replaces
13 pdbx_reference_molecule.replaced_by
14 pdbx_reference_molecule.compound_details
15 pdbx_reference_molecule.description
16 pdbx_reference_molecule.representative_PDB_id_code
17 pdbx_reference_entity_list.prd_id
18 pdbx_reference_entity_list.ref_entity_id
19 pdbx_reference_entity_list.component_id
20 pdbx_reference_entity_list.type
21 pdbx_reference_entity_list.details
22 pdbx_reference_entity_nonpoly.prd_id
23 pdbx_reference_entity_nonpoly.ref_entity_id
24 pdbx_reference_entity_nonpoly.name
25 pdbx_reference_entity_nonpoly.chem_comp_id
26 pdbx_reference_entity_link.prd_id
27 pdbx_reference_entity_link.link_id
28 pdbx_reference_entity_link.link_class
29 pdbx_reference_entity_link.ref_entity_id_1
30 pdbx_reference_entity_link.entity_seq_num_1
31 pdbx_reference_entity_link.comp_id_1
32 pdbx_reference_entity_link.atom_id_1
33 pdbx_reference_entity_link.ref_entity_id_2
34 pdbx_reference_entity_link.entity_seq_num_2
35 pdbx_reference_entity_link.comp_id_2
36 pdbx_reference_entity_link.atom_id_2
37 pdbx_reference_entity_link.value_order
38 pdbx_reference_entity_link.component_1
39 pdbx_reference_entity_link.component_2
40 pdbx_reference_entity_link.details
41 pdbx_reference_entity_poly_link.prd_id
42 pdbx_reference_entity_poly_link.ref_entity_id
43 pdbx_reference_entity_poly_link.link_id
44 pdbx_reference_entity_poly_link.atom_id_1
45 pdbx_reference_entity_poly_link.comp_id_1
46 pdbx_reference_entity_poly_link.entity_seq_num_1
47 pdbx_reference_entity_poly_link.atom_id_2
48 pdbx_reference_entity_poly_link.comp_id_2
49 pdbx_reference_entity_poly_link.entity_seq_num_2
50 pdbx_reference_entity_poly_link.value_order
51 pdbx_reference_entity_poly_link.component_id
52 pdbx_reference_entity_poly.prd_id
53 pdbx_reference_entity_poly.ref_entity_id
54 pdbx_reference_entity_poly.db_code
55 pdbx_reference_entity_poly.db_name
56 pdbx_reference_entity_poly.type
57 pdbx_reference_entity_sequence.prd_id
58 pdbx_reference_entity_sequence.ref_entity_id
59 pdbx_reference_entity_sequence.type
60 pdbx_reference_entity_sequence.NRP_flag
61 pdbx_reference_entity_sequence.one_letter_codes
62 pdbx_reference_entity_poly_seq.prd_id
63 pdbx_reference_entity_poly_seq.ref_entity_id
64 pdbx_reference_entity_poly_seq.num
65 pdbx_reference_entity_poly_seq.mon_id
66 pdbx_reference_entity_poly_seq.parent_mon_id
67 pdbx_reference_entity_poly_seq.hetero
68 pdbx_reference_entity_poly_seq.observed
69 pdbx_reference_entity_src_nat.prd_id
70 pdbx_reference_entity_src_nat.ref_entity_id
71 pdbx_reference_entity_src_nat.ordinal
72 pdbx_reference_entity_src_nat.taxid
73 pdbx_reference_entity_src_nat.organism_scientific
74 pdbx_reference_entity_src_nat.db_code
75 pdbx_reference_entity_src_nat.db_name
76 pdbx_prd_audit.prd_id
77 pdbx_prd_audit.date
78 pdbx_prd_audit.processing_site
79 pdbx_prd_audit.action_type

View File

@@ -0,0 +1,60 @@
chem_comp.id
chem_comp.name
chem_comp.type
chem_comp.pdbx_type
chem_comp.formula
chem_comp.mon_nstd_parent_comp_id
chem_comp.pdbx_synonyms
chem_comp.pdbx_formal_charge
chem_comp.pdbx_initial_date
chem_comp.pdbx_modified_date
chem_comp.pdbx_ambiguous_flag
chem_comp.pdbx_release_status
chem_comp.pdbx_replaced_by
chem_comp.pdbx_replaces
chem_comp.formula_weight
chem_comp.one_letter_code
chem_comp.three_letter_code
chem_comp.pdbx_model_coordinates_details
chem_comp.pdbx_model_coordinates_missing_flag
chem_comp.pdbx_ideal_coordinates_details
chem_comp.pdbx_ideal_coordinates_missing_flag
chem_comp.pdbx_model_coordinates_db_code
chem_comp.pdbx_processing_site
chem_comp_atom.comp_id
chem_comp_atom.atom_id
chem_comp_atom.alt_atom_id
chem_comp_atom.type_symbol
chem_comp_atom.charge
chem_comp_atom.pdbx_align
chem_comp_atom.pdbx_aromatic_flag
chem_comp_atom.pdbx_leaving_atom_flag
chem_comp_atom.pdbx_stereo_config
chem_comp_atom.model_Cartn_x
chem_comp_atom.model_Cartn_y
chem_comp_atom.model_Cartn_z
chem_comp_atom.pdbx_model_Cartn_x_ideal
chem_comp_atom.pdbx_model_Cartn_y_ideal
chem_comp_atom.pdbx_model_Cartn_z_ideal
chem_comp_atom.pdbx_ordinal
chem_comp_bond.comp_id
chem_comp_bond.atom_id_1
chem_comp_bond.atom_id_2
chem_comp_bond.value_order
chem_comp_bond.pdbx_aromatic_flag
chem_comp_bond.pdbx_stereo_config
chem_comp_bond.pdbx_ordinal
pdbx_chem_comp_descriptor.comp_id
pdbx_chem_comp_descriptor.type
pdbx_chem_comp_descriptor.program
pdbx_chem_comp_descriptor.program_version
pdbx_chem_comp_descriptor.descriptor
pdbx_chem_comp_identifier.comp_id
pdbx_chem_comp_identifier.type
pdbx_chem_comp_identifier.program
pdbx_chem_comp_identifier.program_version
pdbx_chem_comp_identifier.identifier
1 chem_comp.id
2 chem_comp.name
3 chem_comp.type
4 chem_comp.pdbx_type
5 chem_comp.formula
6 chem_comp.mon_nstd_parent_comp_id
7 chem_comp.pdbx_synonyms
8 chem_comp.pdbx_formal_charge
9 chem_comp.pdbx_initial_date
10 chem_comp.pdbx_modified_date
11 chem_comp.pdbx_ambiguous_flag
12 chem_comp.pdbx_release_status
13 chem_comp.pdbx_replaced_by
14 chem_comp.pdbx_replaces
15 chem_comp.formula_weight
16 chem_comp.one_letter_code
17 chem_comp.three_letter_code
18 chem_comp.pdbx_model_coordinates_details
19 chem_comp.pdbx_model_coordinates_missing_flag
20 chem_comp.pdbx_ideal_coordinates_details
21 chem_comp.pdbx_ideal_coordinates_missing_flag
22 chem_comp.pdbx_model_coordinates_db_code
23 chem_comp.pdbx_processing_site
24 chem_comp_atom.comp_id
25 chem_comp_atom.atom_id
26 chem_comp_atom.alt_atom_id
27 chem_comp_atom.type_symbol
28 chem_comp_atom.charge
29 chem_comp_atom.pdbx_align
30 chem_comp_atom.pdbx_aromatic_flag
31 chem_comp_atom.pdbx_leaving_atom_flag
32 chem_comp_atom.pdbx_stereo_config
33 chem_comp_atom.model_Cartn_x
34 chem_comp_atom.model_Cartn_y
35 chem_comp_atom.model_Cartn_z
36 chem_comp_atom.pdbx_model_Cartn_x_ideal
37 chem_comp_atom.pdbx_model_Cartn_y_ideal
38 chem_comp_atom.pdbx_model_Cartn_z_ideal
39 chem_comp_atom.pdbx_ordinal
40 chem_comp_bond.comp_id
41 chem_comp_bond.atom_id_1
42 chem_comp_bond.atom_id_2
43 chem_comp_bond.value_order
44 chem_comp_bond.pdbx_aromatic_flag
45 chem_comp_bond.pdbx_stereo_config
46 chem_comp_bond.pdbx_ordinal
47 pdbx_chem_comp_descriptor.comp_id
48 pdbx_chem_comp_descriptor.type
49 pdbx_chem_comp_descriptor.program
50 pdbx_chem_comp_descriptor.program_version
51 pdbx_chem_comp_descriptor.descriptor
52 pdbx_chem_comp_identifier.comp_id
53 pdbx_chem_comp_identifier.type
54 pdbx_chem_comp_identifier.program
55 pdbx_chem_comp_identifier.program_version
56 pdbx_chem_comp_identifier.identifier

View File

@@ -0,0 +1,76 @@
audit.block_doi
database_code.depnum_ccdc_archive
database_code.depnum_ccdc_fiz
database_code.ICSD
database_code.MDF
database_code.NBS
database_code.CSD
database_code.COD
chemical.name_systematic
chemical.name_common
chemical.melting_point
chemical_formula.moiety
chemical_formula.sum
chemical_formula.weight
atom_type.symbol
atom_type.description
atom_type_scat.dispersion_real
atom_type_scat.dispersion_imag
atom_type_scat.source
space_group.crystal_system
space_group.name_H-M_full
space_group.IT_number
space_group_symop.operation_xyz
cell.length_a
cell.length_b
cell.length_c
cell.angle_alpha
cell.angle_beta
cell.angle_gamma
cell.volume
cell.formula_units_Z
atom_site.label
atom_site.type_symbol
atom_site.fract_x
atom_site.fract_y
atom_site.fract_z
atom_site.U_iso_or_equiv
atom_site.adp_type
atom_site.occupancy
atom_site.calc_flag
atom_site.refinement_flags
atom_site.disorder_assembly
atom_site.disorder_group
atom_site.site_symmetry_multiplicity
atom_site_aniso.label
atom_site_aniso.U
atom_site_aniso.U_11
atom_site_aniso.U_22
atom_site_aniso.U_33
atom_site_aniso.U_23
atom_site_aniso.U_13
atom_site_aniso.U_12
atom_site_aniso.U_su
atom_site_aniso.U_11_su
atom_site_aniso.U_22_su
atom_site_aniso.U_33_su
atom_site_aniso.U_23_su
atom_site_aniso.U_13_su
atom_site_aniso.U_12_su
geom_bond.atom_site_label_1
geom_bond.atom_site_label_2
geom_bond.distance
geom_bond.site_symmetry_1
geom_bond.site_symmetry_2
geom_bond.publ_flag
geom_bond.valence
1 audit.block_doi
2 database_code.depnum_ccdc_archive
3 database_code.depnum_ccdc_fiz
4 database_code.ICSD
5 database_code.MDF
6 database_code.NBS
7 database_code.CSD
8 database_code.COD
9 chemical.name_systematic
10 chemical.name_common
11 chemical.melting_point
12 chemical_formula.moiety
13 chemical_formula.sum
14 chemical_formula.weight
15 atom_type.symbol
16 atom_type.description
17 atom_type_scat.dispersion_real
18 atom_type_scat.dispersion_imag
19 atom_type_scat.source
20 space_group.crystal_system
21 space_group.name_H-M_full
22 space_group.IT_number
23 space_group_symop.operation_xyz
24 cell.length_a
25 cell.length_b
26 cell.length_c
27 cell.angle_alpha
28 cell.angle_beta
29 cell.angle_gamma
30 cell.volume
31 cell.formula_units_Z
32 atom_site.label
33 atom_site.type_symbol
34 atom_site.fract_x
35 atom_site.fract_y
36 atom_site.fract_z
37 atom_site.U_iso_or_equiv
38 atom_site.adp_type
39 atom_site.occupancy
40 atom_site.calc_flag
41 atom_site.refinement_flags
42 atom_site.disorder_assembly
43 atom_site.disorder_group
44 atom_site.site_symmetry_multiplicity
45 atom_site_aniso.label
46 atom_site_aniso.U
47 atom_site_aniso.U_11
48 atom_site_aniso.U_22
49 atom_site_aniso.U_33
50 atom_site_aniso.U_23
51 atom_site_aniso.U_13
52 atom_site_aniso.U_12
53 atom_site_aniso.U_su
54 atom_site_aniso.U_11_su
55 atom_site_aniso.U_22_su
56 atom_site_aniso.U_33_su
57 atom_site_aniso.U_23_su
58 atom_site_aniso.U_13_su
59 atom_site_aniso.U_12_su
60 geom_bond.atom_site_label_1
61 geom_bond.atom_site_label_2
62 geom_bond.distance
63 geom_bond.site_symmetry_1
64 geom_bond.site_symmetry_2
65 geom_bond.publ_flag
66 geom_bond.valence

View File

@@ -0,0 +1,805 @@
atom_sites.entry_id
atom_sites.fract_transf_matrix
atom_sites.fract_transf_vector
atom_site.group_PDB
atom_site.id
atom_site.type_symbol
atom_site.label_atom_id
atom_site.label_alt_id
atom_site.label_comp_id
atom_site.label_asym_id
atom_site.label_entity_id
atom_site.label_seq_id
atom_site.pdbx_PDB_ins_code
atom_site.pdbx_formal_charge
atom_site.Cartn_x
atom_site.Cartn_y
atom_site.Cartn_z
atom_site.occupancy
atom_site.B_iso_or_equiv
atom_site.auth_atom_id
atom_site.auth_comp_id
atom_site.auth_asym_id
atom_site.auth_seq_id
atom_site.pdbx_PDB_model_num
atom_site.ihm_model_id
atom_site_anisotrop.id
atom_site_anisotrop.U
atom_site_anisotrop.U_esd
atom_site_anisotrop.pdbx_PDB_ins_code
atom_site_anisotrop.pdbx_auth_asym_id
atom_site_anisotrop.pdbx_auth_atom_id
atom_site_anisotrop.pdbx_auth_comp_id
atom_site_anisotrop.pdbx_auth_seq_id
atom_site_anisotrop.pdbx_label_alt_id
atom_site_anisotrop.pdbx_label_asym_id
atom_site_anisotrop.pdbx_label_atom_id
atom_site_anisotrop.pdbx_label_comp_id
atom_site_anisotrop.pdbx_label_seq_id
atom_site_anisotrop.type_symbol
chem_comp.id
chem_comp.type
chem_comp.mon_nstd_flag
chem_comp.name
chem_comp.pdbx_synonyms
chem_comp.formula
chem_comp.formula_weight
chem_comp_bond.comp_id
chem_comp_bond.pdbx_stereo_config
chem_comp_bond.pdbx_ordinal
chem_comp_bond.pdbx_aromatic_flag
chem_comp_bond.atom_id_1
chem_comp_bond.atom_id_2
chem_comp_bond.value_order
pdbx_chem_comp_identifier.comp_id
pdbx_chem_comp_identifier.type
pdbx_chem_comp_identifier.program
pdbx_chem_comp_identifier.program_version
pdbx_chem_comp_identifier.identifier
pdbx_chem_comp_related.comp_id
pdbx_chem_comp_related.related_comp_id
pdbx_chem_comp_related.relationship_type
pdbx_chem_comp_related.details
pdbx_chem_comp_synonyms.comp_id
pdbx_chem_comp_synonyms.name
pdbx_chem_comp_synonyms.provenance
cell.entry_id
cell.length_a
cell.length_b
cell.length_c
cell.angle_alpha
cell.angle_beta
cell.angle_gamma
cell.Z_PDB
cell.pdbx_unique_axis
pdbx_database_related.db_name
pdbx_database_related.details
pdbx_database_related.db_id
pdbx_database_related.content_type
pdbx_database_status.status_code
pdbx_database_status.status_code_sf
pdbx_database_status.status_code_mr
pdbx_database_status.entry_id
pdbx_database_status.recvd_initial_deposition_date
pdbx_database_status.SG_entry
pdbx_database_status.deposit_site
pdbx_database_status.process_site
pdbx_database_status.status_code_cs
pdbx_database_status.methods_development_category
pdbx_database_status.pdb_format_compatible
entity.id
entity.type
entity.src_method
entity.pdbx_description
entity.formula_weight
entity.pdbx_number_of_molecules
entity.details
entity.pdbx_mutation
entity.pdbx_fragment
entity.pdbx_ec
entity_poly.entity_id
entity_poly.type
entity_poly.nstd_linkage
entity_poly.nstd_monomer
entity_poly.pdbx_seq_one_letter_code
entity_poly.pdbx_seq_one_letter_code_can
entity_poly.pdbx_strand_id
entity_poly.pdbx_target_identifier
entity_poly_seq.entity_id
entity_poly_seq.num
entity_poly_seq.mon_id
entity_poly_seq.hetero
entity_src_gen.entity_id
entity_src_gen.pdbx_src_id
entity_src_gen.pdbx_beg_seq_num
entity_src_gen.pdbx_end_seq_num
entity_src_gen.pdbx_gene_src_gene
entity_src_gen.pdbx_gene_src_scientific_name
entity_src_gen.plasmid_name
entity_src_nat.entity_id
entity_src_nat.pdbx_src_id
entity_src_nat.pdbx_beg_seq_num
entity_src_nat.pdbx_end_seq_num
entity_src_nat.pdbx_organism_scientific
entity_src_nat.pdbx_plasmid_name
pdbx_entity_instance_feature.ordinal
pdbx_entity_instance_feature.feature_type
pdbx_entity_instance_feature.details
pdbx_entity_instance_feature.asym_id
pdbx_entity_instance_feature.comp_id
pdbx_entity_instance_feature.seq_num
pdbx_entity_instance_feature.auth_asym_id
pdbx_entity_instance_feature.auth_comp_id
pdbx_entity_instance_feature.auth_seq_num
pdbx_entity_src_syn.entity_id
pdbx_entity_src_syn.pdbx_src_id
pdbx_entity_src_syn.pdbx_beg_seq_num
pdbx_entity_src_syn.pdbx_end_seq_num
pdbx_entity_src_syn.organism_scientific
pdbx_entity_branch.entity_id
pdbx_entity_branch.type
pdbx_entity_branch_list.entity_id
pdbx_entity_branch_list.comp_id
pdbx_entity_branch_list.num
pdbx_entity_branch_list.hetero
pdbx_entity_branch_link.link_id
pdbx_entity_branch_link.entity_id
pdbx_entity_branch_link.entity_branch_list_num_1
pdbx_entity_branch_link.comp_id_1
pdbx_entity_branch_link.atom_id_1
pdbx_entity_branch_link.leaving_atom_id_1
pdbx_entity_branch_link.atom_stereo_config_1
pdbx_entity_branch_link.entity_branch_list_num_2
pdbx_entity_branch_link.comp_id_2
pdbx_entity_branch_link.atom_id_2
pdbx_entity_branch_link.leaving_atom_id_2
pdbx_entity_branch_link.atom_stereo_config_2
pdbx_entity_branch_link.value_order
pdbx_entity_branch_link.details
pdbx_branch_scheme.asym_id
pdbx_branch_scheme.entity_id
pdbx_branch_scheme.mon_id
pdbx_branch_scheme.num
pdbx_branch_scheme.auth_asym_id
pdbx_branch_scheme.auth_mon_id
pdbx_branch_scheme.auth_seq_num
pdbx_branch_scheme.hetero
pdbx_branch_scheme.pdb_mon_id
pdbx_branch_scheme.pdb_asym_id
pdbx_branch_scheme.pdb_seq_num
pdbx_entity_branch_descriptor.ordinal
pdbx_entity_branch_descriptor.entity_id
pdbx_entity_branch_descriptor.descriptor
pdbx_entity_branch_descriptor.type
pdbx_entity_branch_descriptor.program
pdbx_entity_branch_descriptor.program_version
pdbx_entity_nonpoly.entity_id
pdbx_entity_nonpoly.name
pdbx_entity_nonpoly.comp_id
pdbx_nonpoly_scheme.asym_id
pdbx_nonpoly_scheme.entity_id
pdbx_nonpoly_scheme.mon_id
pdbx_nonpoly_scheme.ndb_seq_num
pdbx_nonpoly_scheme.pdb_seq_num
pdbx_nonpoly_scheme.auth_seq_num
pdbx_nonpoly_scheme.pdb_mon_id
pdbx_nonpoly_scheme.auth_mon_id
pdbx_nonpoly_scheme.pdb_strand_id
pdbx_nonpoly_scheme.pdb_ins_code
entry.id
audit_conform.dict_name
audit_conform.dict_version
audit_conform.dict_location
database_2.database_id
database_2.database_code
audit_author.name
audit_author.pdbx_ordinal
audit_author.identifier_ORCID
citation.id
citation.title
citation.journal_abbrev
citation.journal_volume
citation.page_first
citation.page_last
citation.year
citation.journal_id_ASTM
citation.country
citation.journal_id_ISSN
citation.journal_id_CSD
citation.book_publisher
citation.pdbx_database_id_PubMed
citation.pdbx_database_id_DOI
citation_author.citation_id
citation_author.name
citation_author.ordinal
exptl.entry_id
exptl.method
struct.entry_id
struct.title
struct.pdbx_descriptor
struct_asym.id
struct_asym.pdbx_blank_PDB_chainid_flag
struct_asym.pdbx_modified
struct_asym.entity_id
struct_asym.details
struct_conf.conf_type_id
struct_conf.id
struct_conf.pdbx_PDB_helix_id
struct_conf.beg_label_comp_id
struct_conf.beg_label_asym_id
struct_conf.beg_label_seq_id
struct_conf.pdbx_beg_PDB_ins_code
struct_conf.end_label_comp_id
struct_conf.end_label_asym_id
struct_conf.end_label_seq_id
struct_conf.pdbx_end_PDB_ins_code
struct_conf.beg_auth_comp_id
struct_conf.beg_auth_asym_id
struct_conf.beg_auth_seq_id
struct_conf.end_auth_comp_id
struct_conf.end_auth_asym_id
struct_conf.end_auth_seq_id
struct_conf.pdbx_PDB_helix_class
struct_conf.details
struct_conf.pdbx_PDB_helix_length
struct_conn.id
struct_conn.conn_type_id
struct_conn.pdbx_PDB_id
struct_conn.ptnr1_label_asym_id
struct_conn.ptnr1_label_comp_id
struct_conn.ptnr1_label_seq_id
struct_conn.ptnr1_label_atom_id
struct_conn.pdbx_ptnr1_label_alt_id
struct_conn.pdbx_ptnr1_PDB_ins_code
struct_conn.pdbx_ptnr1_standard_comp_id
struct_conn.ptnr1_symmetry
struct_conn.ptnr2_label_asym_id
struct_conn.ptnr2_label_comp_id
struct_conn.ptnr2_label_seq_id
struct_conn.ptnr2_label_atom_id
struct_conn.pdbx_ptnr2_label_alt_id
struct_conn.pdbx_ptnr2_PDB_ins_code
struct_conn.ptnr1_auth_asym_id
struct_conn.ptnr1_auth_comp_id
struct_conn.ptnr1_auth_seq_id
struct_conn.ptnr2_auth_asym_id
struct_conn.ptnr2_auth_comp_id
struct_conn.ptnr2_auth_seq_id
struct_conn.ptnr2_symmetry
struct_conn.pdbx_ptnr3_label_atom_id
struct_conn.pdbx_ptnr3_label_seq_id
struct_conn.pdbx_ptnr3_label_comp_id
struct_conn.pdbx_ptnr3_label_asym_id
struct_conn.pdbx_ptnr3_label_alt_id
struct_conn.pdbx_ptnr3_PDB_ins_code
struct_conn.details
struct_conn.pdbx_dist_value
struct_conn.pdbx_value_order
struct_conn_type.id
struct_conn_type.criteria
struct_conn_type.reference
struct_keywords.entry_id
struct_keywords.pdbx_keywords
struct_keywords.text
struct_ncs_oper.id
struct_ncs_oper.code
struct_ncs_oper.matrix
struct_ncs_oper.vector
struct_ncs_oper.details
struct_sheet_range.sheet_id
struct_sheet_range.id
struct_sheet_range.beg_label_comp_id
struct_sheet_range.beg_label_asym_id
struct_sheet_range.beg_label_seq_id
struct_sheet_range.pdbx_beg_PDB_ins_code
struct_sheet_range.end_label_comp_id
struct_sheet_range.end_label_asym_id
struct_sheet_range.end_label_seq_id
struct_sheet_range.pdbx_end_PDB_ins_code
struct_sheet_range.beg_auth_comp_id
struct_sheet_range.beg_auth_asym_id
struct_sheet_range.beg_auth_seq_id
struct_sheet_range.end_auth_comp_id
struct_sheet_range.end_auth_asym_id
struct_sheet_range.end_auth_seq_id
struct_site.id
struct_site.pdbx_evidence_code
struct_site.pdbx_auth_asym_id
struct_site.pdbx_auth_comp_id
struct_site.pdbx_auth_seq_id
struct_site.pdbx_auth_ins_code
struct_site.pdbx_num_residues
struct_site.details
struct_site_gen.id
struct_site_gen.site_id
struct_site_gen.pdbx_num_res
struct_site_gen.label_comp_id
struct_site_gen.label_asym_id
struct_site_gen.label_seq_id
struct_site_gen.pdbx_auth_ins_code
struct_site_gen.auth_comp_id
struct_site_gen.auth_asym_id
struct_site_gen.auth_seq_id
struct_site_gen.label_atom_id
struct_site_gen.label_alt_id
struct_site_gen.symmetry
struct_site_gen.details
symmetry.entry_id
symmetry.cell_setting
symmetry.Int_Tables_number
symmetry.space_group_name_Hall
symmetry.space_group_name_H-M
pdbx_molecule.instance_id
pdbx_molecule.prd_id
pdbx_molecule.asym_id
pdbx_molecule_features.prd_id
pdbx_molecule_features.name
pdbx_molecule_features.type
pdbx_molecule_features.class
pdbx_molecule_features.details
pdbx_reference_entity_link.prd_id
pdbx_reference_entity_link.link_id
pdbx_reference_entity_link.link_class
pdbx_reference_entity_link.ref_entity_id_1
pdbx_reference_entity_link.entity_seq_num_1
pdbx_reference_entity_link.comp_id_1
pdbx_reference_entity_link.atom_id_1
pdbx_reference_entity_link.ref_entity_id_2
pdbx_reference_entity_link.entity_seq_num_2
pdbx_reference_entity_link.comp_id_2
pdbx_reference_entity_link.atom_id_2
pdbx_reference_entity_link.value_order
pdbx_reference_entity_link.component_1
pdbx_reference_entity_link.component_2
pdbx_reference_entity_link.details
pdbx_reference_entity_list.prd_id
pdbx_reference_entity_list.ref_entity_id
pdbx_reference_entity_list.component_id
pdbx_reference_entity_list.type
pdbx_reference_entity_list.details
pdbx_reference_entity_poly_link.prd_id
pdbx_reference_entity_poly_link.ref_entity_id
pdbx_reference_entity_poly_link.link_id
pdbx_reference_entity_poly_link.atom_id_1
pdbx_reference_entity_poly_link.comp_id_1
pdbx_reference_entity_poly_link.entity_seq_num_1
pdbx_reference_entity_poly_link.atom_id_2
pdbx_reference_entity_poly_link.comp_id_2
pdbx_reference_entity_poly_link.entity_seq_num_2
pdbx_reference_entity_poly_link.value_order
pdbx_reference_entity_poly_link.component_id
pdbx_struct_assembly.id
pdbx_struct_assembly.details
pdbx_struct_assembly.method_details
pdbx_struct_assembly.oligomeric_details
pdbx_struct_assembly.oligomeric_count
pdbx_struct_assembly_gen.assembly_id
pdbx_struct_assembly_gen.oper_expression
pdbx_struct_assembly_gen.asym_id_list
pdbx_struct_oper_list.id
pdbx_struct_oper_list.type
pdbx_struct_oper_list.name
pdbx_struct_oper_list.symmetry_operation
pdbx_struct_oper_list.matrix
pdbx_struct_oper_list.vector
pdbx_struct_mod_residue.id
pdbx_struct_mod_residue.label_asym_id
pdbx_struct_mod_residue.label_seq_id
pdbx_struct_mod_residue.label_comp_id
pdbx_struct_mod_residue.auth_asym_id
pdbx_struct_mod_residue.auth_seq_id
pdbx_struct_mod_residue.auth_comp_id
pdbx_struct_mod_residue.PDB_ins_code
pdbx_struct_mod_residue.parent_comp_id
pdbx_struct_mod_residue.details
pdbx_unobs_or_zero_occ_residues.id
pdbx_unobs_or_zero_occ_residues.PDB_model_num
pdbx_unobs_or_zero_occ_residues.polymer_flag
pdbx_unobs_or_zero_occ_residues.occupancy_flag
pdbx_unobs_or_zero_occ_residues.auth_asym_id
pdbx_unobs_or_zero_occ_residues.auth_comp_id
pdbx_unobs_or_zero_occ_residues.auth_seq_id
pdbx_unobs_or_zero_occ_residues.PDB_ins_code
pdbx_unobs_or_zero_occ_residues.label_asym_id
pdbx_unobs_or_zero_occ_residues.label_comp_id
pdbx_unobs_or_zero_occ_residues.label_seq_id
ihm_struct_assembly.id
ihm_struct_assembly.name
ihm_struct_assembly.description
ihm_struct_assembly_details.id
ihm_struct_assembly_details.assembly_id
ihm_struct_assembly_details.parent_assembly_id
ihm_struct_assembly_details.entity_description
ihm_struct_assembly_details.entity_id
ihm_struct_assembly_details.asym_id
ihm_struct_assembly_details.entity_poly_segment_id
ihm_model_representation.id
ihm_model_representation.name
ihm_model_representation.details
ihm_model_representation_details.id
ihm_model_representation_details.representation_id
ihm_model_representation_details.entity_id
ihm_model_representation_details.entity_description
ihm_model_representation_details.entity_asym_id
ihm_model_representation_details.entity_poly_segment_id
ihm_model_representation_details.model_object_primitive
ihm_model_representation_details.starting_model_id
ihm_model_representation_details.model_mode
ihm_model_representation_details.model_granularity
ihm_model_representation_details.model_object_count
ihm_external_reference_info.reference_id
ihm_external_reference_info.reference_provider
ihm_external_reference_info.reference_type
ihm_external_reference_info.reference
ihm_external_reference_info.refers_to
ihm_external_reference_info.associated_url
ihm_external_files.id
ihm_external_files.reference_id
ihm_external_files.file_path
ihm_external_files.content_type
ihm_external_files.file_size_bytes
ihm_external_files.details
ihm_dataset_list.id
ihm_dataset_list.data_type
ihm_dataset_list.database_hosted
ihm_dataset_group.id
ihm_dataset_group.name
ihm_dataset_group.application
ihm_dataset_group.details
ihm_dataset_group_link.group_id
ihm_dataset_group_link.dataset_list_id
ihm_dataset_external_reference.id
ihm_dataset_external_reference.dataset_list_id
ihm_dataset_external_reference.file_id
ihm_dataset_related_db_reference.id
ihm_dataset_related_db_reference.dataset_list_id
ihm_dataset_related_db_reference.db_name
ihm_dataset_related_db_reference.accession_code
ihm_dataset_related_db_reference.version
ihm_dataset_related_db_reference.details
ihm_related_datasets.dataset_list_id_derived
ihm_related_datasets.dataset_list_id_primary
ihm_poly_residue_feature.ordinal_id
ihm_poly_residue_feature.feature_id
ihm_poly_residue_feature.entity_id
ihm_poly_residue_feature.asym_id
ihm_poly_residue_feature.seq_id_begin
ihm_poly_residue_feature.comp_id_begin
ihm_poly_residue_feature.seq_id_end
ihm_poly_residue_feature.comp_id_end
ihm_feature_list.feature_id
ihm_feature_list.feature_type
ihm_feature_list.entity_type
ihm_cross_link_list.id
ihm_cross_link_list.group_id
ihm_cross_link_list.entity_description_1
ihm_cross_link_list.entity_id_1
ihm_cross_link_list.seq_id_1
ihm_cross_link_list.comp_id_1
ihm_cross_link_list.entity_description_2
ihm_cross_link_list.entity_id_2
ihm_cross_link_list.seq_id_2
ihm_cross_link_list.comp_id_2
ihm_cross_link_list.linker_type
ihm_cross_link_list.dataset_list_id
ihm_cross_link_restraint.id
ihm_cross_link_restraint.group_id
ihm_cross_link_restraint.entity_id_1
ihm_cross_link_restraint.asym_id_1
ihm_cross_link_restraint.seq_id_1
ihm_cross_link_restraint.atom_id_1
ihm_cross_link_restraint.comp_id_1
ihm_cross_link_restraint.entity_id_2
ihm_cross_link_restraint.asym_id_2
ihm_cross_link_restraint.seq_id_2
ihm_cross_link_restraint.atom_id_2
ihm_cross_link_restraint.comp_id_2
ihm_cross_link_restraint.restraint_type
ihm_cross_link_restraint.conditional_crosslink_flag
ihm_cross_link_restraint.model_granularity
ihm_cross_link_restraint.distance_threshold
ihm_cross_link_restraint.psi
ihm_cross_link_restraint.sigma_1
ihm_cross_link_restraint.sigma_2
ihm_cross_link_result_parameters.id
ihm_cross_link_result_parameters.restraint_id
ihm_cross_link_result_parameters.model_id
ihm_cross_link_result_parameters.psi
ihm_cross_link_result_parameters.sigma_1
ihm_cross_link_result_parameters.sigma_2
ihm_sas_restraint.id
ihm_sas_restraint.dataset_list_id
ihm_sas_restraint.model_id
ihm_sas_restraint.struct_assembly_id
ihm_sas_restraint.profile_segment_flag
ihm_sas_restraint.fitting_atom_type
ihm_sas_restraint.fitting_method
ihm_sas_restraint.fitting_state
ihm_sas_restraint.radius_of_gyration
ihm_sas_restraint.chi_value
ihm_sas_restraint.details
ihm_derived_distance_restraint.id
ihm_derived_distance_restraint.group_id
ihm_derived_distance_restraint.feature_id_1
ihm_derived_distance_restraint.feature_id_2
ihm_derived_distance_restraint.group_conditionality
ihm_derived_distance_restraint.restraint_type
ihm_derived_distance_restraint.distance_upper_limit
ihm_derived_distance_restraint.random_exclusion_fraction
ihm_derived_distance_restraint.dataset_list_id
ihm_2dem_class_average_restraint.id
ihm_2dem_class_average_restraint.dataset_list_id
ihm_2dem_class_average_restraint.number_raw_micrographs
ihm_2dem_class_average_restraint.pixel_size_width
ihm_2dem_class_average_restraint.pixel_size_height
ihm_2dem_class_average_restraint.image_resolution
ihm_2dem_class_average_restraint.image_segment_flag
ihm_2dem_class_average_restraint.number_of_projections
ihm_2dem_class_average_restraint.struct_assembly_id
ihm_2dem_class_average_restraint.details
ihm_2dem_class_average_fitting.id
ihm_2dem_class_average_fitting.restraint_id
ihm_2dem_class_average_fitting.model_id
ihm_2dem_class_average_fitting.cross_correlation_coefficient
ihm_2dem_class_average_fitting.rot_matrix
ihm_2dem_class_average_fitting.tr_vector
ihm_3dem_restraint.id
ihm_3dem_restraint.dataset_list_id
ihm_3dem_restraint.fitting_method
ihm_3dem_restraint.struct_assembly_id
ihm_3dem_restraint.number_of_gaussians
ihm_3dem_restraint.model_id
ihm_3dem_restraint.cross_correlation_coefficient
ihm_predicted_contact_restraint.id
ihm_predicted_contact_restraint.group_id
ihm_predicted_contact_restraint.entity_id_1
ihm_predicted_contact_restraint.asym_id_1
ihm_predicted_contact_restraint.seq_id_1
ihm_predicted_contact_restraint.comp_id_1
ihm_predicted_contact_restraint.rep_atom_1
ihm_predicted_contact_restraint.entity_id_2
ihm_predicted_contact_restraint.asym_id_2
ihm_predicted_contact_restraint.seq_id_2
ihm_predicted_contact_restraint.comp_id_2
ihm_predicted_contact_restraint.rep_atom_2
ihm_predicted_contact_restraint.restraint_type
ihm_predicted_contact_restraint.distance_lower_limit
ihm_predicted_contact_restraint.distance_upper_limit
ihm_predicted_contact_restraint.probability
ihm_predicted_contact_restraint.model_granularity
ihm_predicted_contact_restraint.dataset_list_id
ihm_predicted_contact_restraint.software_id
ihm_starting_model_details.starting_model_id
ihm_starting_model_details.entity_id
ihm_starting_model_details.entity_description
ihm_starting_model_details.asym_id
ihm_starting_model_details.entity_poly_segment_id
ihm_starting_model_details.starting_model_source
ihm_starting_model_details.starting_model_auth_asym_id
ihm_starting_model_details.starting_model_sequence_offset
ihm_starting_model_details.dataset_list_id
ihm_starting_comparative_models.id
ihm_starting_comparative_models.starting_model_id
ihm_starting_comparative_models.starting_model_auth_asym_id
ihm_starting_comparative_models.starting_model_seq_id_begin
ihm_starting_comparative_models.starting_model_seq_id_end
ihm_starting_comparative_models.template_auth_asym_id
ihm_starting_comparative_models.template_seq_id_begin
ihm_starting_comparative_models.template_seq_id_end
ihm_starting_comparative_models.template_sequence_identity
ihm_starting_comparative_models.template_sequence_identity_denominator
ihm_starting_comparative_models.template_dataset_list_id
ihm_starting_comparative_models.alignment_file_id
ihm_starting_model_coord.starting_model_id
ihm_starting_model_coord.group_PDB
ihm_starting_model_coord.id
ihm_starting_model_coord.type_symbol
ihm_starting_model_coord.atom_id
ihm_starting_model_coord.comp_id
ihm_starting_model_coord.entity_id
ihm_starting_model_coord.asym_id
ihm_starting_model_coord.seq_id
ihm_starting_model_coord.Cartn_x
ihm_starting_model_coord.Cartn_y
ihm_starting_model_coord.Cartn_z
ihm_starting_model_coord.B_iso_or_equiv
ihm_starting_model_coord.ordinal_id
ihm_starting_model_seq_dif.id
ihm_starting_model_seq_dif.entity_id
ihm_starting_model_seq_dif.asym_id
ihm_starting_model_seq_dif.seq_id
ihm_starting_model_seq_dif.comp_id
ihm_starting_model_seq_dif.starting_model_id
ihm_starting_model_seq_dif.db_asym_id
ihm_starting_model_seq_dif.db_seq_id
ihm_starting_model_seq_dif.db_comp_id
ihm_starting_model_seq_dif.details
ihm_modeling_protocol.id
ihm_modeling_protocol.protocol_name
ihm_modeling_protocol.num_steps
ihm_modeling_protocol_details.id
ihm_modeling_protocol_details.protocol_id
ihm_modeling_protocol_details.step_id
ihm_modeling_protocol_details.struct_assembly_id
ihm_modeling_protocol_details.dataset_group_id
ihm_modeling_protocol_details.struct_assembly_description
ihm_modeling_protocol_details.step_name
ihm_modeling_protocol_details.step_method
ihm_modeling_protocol_details.num_models_begin
ihm_modeling_protocol_details.num_models_end
ihm_modeling_protocol_details.multi_scale_flag
ihm_modeling_protocol_details.multi_state_flag
ihm_modeling_protocol_details.ordered_flag
ihm_modeling_protocol_details.software_id
ihm_modeling_protocol_details.script_file_id
ihm_modeling_post_process.id
ihm_modeling_post_process.protocol_id
ihm_modeling_post_process.analysis_id
ihm_modeling_post_process.step_id
ihm_modeling_post_process.type
ihm_modeling_post_process.feature
ihm_modeling_post_process.num_models_begin
ihm_modeling_post_process.num_models_end
ihm_ensemble_info.ensemble_id
ihm_ensemble_info.ensemble_name
ihm_ensemble_info.post_process_id
ihm_ensemble_info.model_group_id
ihm_ensemble_info.ensemble_clustering_method
ihm_ensemble_info.ensemble_clustering_feature
ihm_ensemble_info.num_ensemble_models
ihm_ensemble_info.num_ensemble_models_deposited
ihm_ensemble_info.ensemble_precision_value
ihm_ensemble_info.ensemble_file_id
ihm_localization_density_files.id
ihm_localization_density_files.file_id
ihm_localization_density_files.ensemble_id
ihm_localization_density_files.entity_id
ihm_localization_density_files.asym_id
ihm_localization_density_files.entity_poly_segment_id
ihm_model_list.model_id
ihm_model_list.model_name
ihm_model_list.assembly_id
ihm_model_list.protocol_id
ihm_model_list.representation_id
ihm_model_group.id
ihm_model_group.name
ihm_model_group.details
ihm_model_group_link.group_id
ihm_model_group_link.model_id
ihm_model_representative.id
ihm_model_representative.model_group_id
ihm_model_representative.model_id
ihm_model_representative.selection_criteria
ihm_sphere_obj_site.id
ihm_sphere_obj_site.entity_id
ihm_sphere_obj_site.seq_id_begin
ihm_sphere_obj_site.seq_id_end
ihm_sphere_obj_site.asym_id
ihm_sphere_obj_site.Cartn_x
ihm_sphere_obj_site.Cartn_y
ihm_sphere_obj_site.Cartn_z
ihm_sphere_obj_site.object_radius
ihm_sphere_obj_site.rmsf
ihm_sphere_obj_site.model_id
ihm_gaussian_obj_site.id
ihm_gaussian_obj_site.entity_id
ihm_gaussian_obj_site.seq_id_begin
ihm_gaussian_obj_site.seq_id_end
ihm_gaussian_obj_site.asym_id
ihm_gaussian_obj_site.mean_Cartn_x
ihm_gaussian_obj_site.mean_Cartn_y
ihm_gaussian_obj_site.mean_Cartn_z
ihm_gaussian_obj_site.weight
ihm_gaussian_obj_site.covariance_matrix
ihm_gaussian_obj_site.model_id
ihm_gaussian_obj_ensemble.id
ihm_gaussian_obj_ensemble.entity_id
ihm_gaussian_obj_ensemble.seq_id_begin
ihm_gaussian_obj_ensemble.seq_id_end
ihm_gaussian_obj_ensemble.asym_id
ihm_gaussian_obj_ensemble.mean_Cartn_x
ihm_gaussian_obj_ensemble.mean_Cartn_y
ihm_gaussian_obj_ensemble.mean_Cartn_z
ihm_gaussian_obj_ensemble.weight
ihm_gaussian_obj_ensemble.covariance_matrix
ihm_gaussian_obj_ensemble.ensemble_id
ihm_multi_state_modeling.state_id
ihm_multi_state_modeling.state_group_id
ihm_multi_state_modeling.population_fraction
ihm_multi_state_modeling.population_fraction_sd
ihm_multi_state_modeling.state_type
ihm_multi_state_modeling.state_name
ihm_multi_state_modeling.experiment_type
ihm_multi_state_modeling.details
1 atom_sites.entry_id
2 atom_sites.fract_transf_matrix
3 atom_sites.fract_transf_vector
4 atom_site.group_PDB
5 atom_site.id
6 atom_site.type_symbol
7 atom_site.label_atom_id
8 atom_site.label_alt_id
9 atom_site.label_comp_id
10 atom_site.label_asym_id
11 atom_site.label_entity_id
12 atom_site.label_seq_id
13 atom_site.pdbx_PDB_ins_code
14 atom_site.pdbx_formal_charge
15 atom_site.Cartn_x
16 atom_site.Cartn_y
17 atom_site.Cartn_z
18 atom_site.occupancy
19 atom_site.B_iso_or_equiv
20 atom_site.auth_atom_id
21 atom_site.auth_comp_id
22 atom_site.auth_asym_id
23 atom_site.auth_seq_id
24 atom_site.pdbx_PDB_model_num
25 atom_site.ihm_model_id
26 atom_site_anisotrop.id
27 atom_site_anisotrop.U
28 atom_site_anisotrop.U_esd
29 atom_site_anisotrop.pdbx_PDB_ins_code
30 atom_site_anisotrop.pdbx_auth_asym_id
31 atom_site_anisotrop.pdbx_auth_atom_id
32 atom_site_anisotrop.pdbx_auth_comp_id
33 atom_site_anisotrop.pdbx_auth_seq_id
34 atom_site_anisotrop.pdbx_label_alt_id
35 atom_site_anisotrop.pdbx_label_asym_id
36 atom_site_anisotrop.pdbx_label_atom_id
37 atom_site_anisotrop.pdbx_label_comp_id
38 atom_site_anisotrop.pdbx_label_seq_id
39 atom_site_anisotrop.type_symbol
40 chem_comp.id
41 chem_comp.type
42 chem_comp.mon_nstd_flag
43 chem_comp.name
44 chem_comp.pdbx_synonyms
45 chem_comp.formula
46 chem_comp.formula_weight
47 chem_comp_bond.comp_id
48 chem_comp_bond.pdbx_stereo_config
49 chem_comp_bond.pdbx_ordinal
50 chem_comp_bond.pdbx_aromatic_flag
51 chem_comp_bond.atom_id_1
52 chem_comp_bond.atom_id_2
53 chem_comp_bond.value_order
54 pdbx_chem_comp_identifier.comp_id
55 pdbx_chem_comp_identifier.type
56 pdbx_chem_comp_identifier.program
57 pdbx_chem_comp_identifier.program_version
58 pdbx_chem_comp_identifier.identifier
59 pdbx_chem_comp_related.comp_id
60 pdbx_chem_comp_related.related_comp_id
61 pdbx_chem_comp_related.relationship_type
62 pdbx_chem_comp_related.details
63 pdbx_chem_comp_synonyms.comp_id
64 pdbx_chem_comp_synonyms.name
65 pdbx_chem_comp_synonyms.provenance
66 cell.entry_id
67 cell.length_a
68 cell.length_b
69 cell.length_c
70 cell.angle_alpha
71 cell.angle_beta
72 cell.angle_gamma
73 cell.Z_PDB
74 cell.pdbx_unique_axis
75 pdbx_database_related.db_name
76 pdbx_database_related.details
77 pdbx_database_related.db_id
78 pdbx_database_related.content_type
79 pdbx_database_status.status_code
80 pdbx_database_status.status_code_sf
81 pdbx_database_status.status_code_mr
82 pdbx_database_status.entry_id
83 pdbx_database_status.recvd_initial_deposition_date
84 pdbx_database_status.SG_entry
85 pdbx_database_status.deposit_site
86 pdbx_database_status.process_site
87 pdbx_database_status.status_code_cs
88 pdbx_database_status.methods_development_category
89 pdbx_database_status.pdb_format_compatible
90 entity.id
91 entity.type
92 entity.src_method
93 entity.pdbx_description
94 entity.formula_weight
95 entity.pdbx_number_of_molecules
96 entity.details
97 entity.pdbx_mutation
98 entity.pdbx_fragment
99 entity.pdbx_ec
100 entity_poly.entity_id
101 entity_poly.type
102 entity_poly.nstd_linkage
103 entity_poly.nstd_monomer
104 entity_poly.pdbx_seq_one_letter_code
105 entity_poly.pdbx_seq_one_letter_code_can
106 entity_poly.pdbx_strand_id
107 entity_poly.pdbx_target_identifier
108 entity_poly_seq.entity_id
109 entity_poly_seq.num
110 entity_poly_seq.mon_id
111 entity_poly_seq.hetero
112 entity_src_gen.entity_id
113 entity_src_gen.pdbx_src_id
114 entity_src_gen.pdbx_beg_seq_num
115 entity_src_gen.pdbx_end_seq_num
116 entity_src_gen.pdbx_gene_src_gene
117 entity_src_gen.pdbx_gene_src_scientific_name
118 entity_src_gen.plasmid_name
119 entity_src_nat.entity_id
120 entity_src_nat.pdbx_src_id
121 entity_src_nat.pdbx_beg_seq_num
122 entity_src_nat.pdbx_end_seq_num
123 entity_src_nat.pdbx_organism_scientific
124 entity_src_nat.pdbx_plasmid_name
125 pdbx_entity_instance_feature.ordinal
126 pdbx_entity_instance_feature.feature_type
127 pdbx_entity_instance_feature.details
128 pdbx_entity_instance_feature.asym_id
129 pdbx_entity_instance_feature.comp_id
130 pdbx_entity_instance_feature.seq_num
131 pdbx_entity_instance_feature.auth_asym_id
132 pdbx_entity_instance_feature.auth_comp_id
133 pdbx_entity_instance_feature.auth_seq_num
134 pdbx_entity_src_syn.entity_id
135 pdbx_entity_src_syn.pdbx_src_id
136 pdbx_entity_src_syn.pdbx_beg_seq_num
137 pdbx_entity_src_syn.pdbx_end_seq_num
138 pdbx_entity_src_syn.organism_scientific
139 pdbx_entity_branch.entity_id
140 pdbx_entity_branch.type
141 pdbx_entity_branch_list.entity_id
142 pdbx_entity_branch_list.comp_id
143 pdbx_entity_branch_list.num
144 pdbx_entity_branch_list.hetero
145 pdbx_entity_branch_link.link_id
146 pdbx_entity_branch_link.entity_id
147 pdbx_entity_branch_link.entity_branch_list_num_1
148 pdbx_entity_branch_link.comp_id_1
149 pdbx_entity_branch_link.atom_id_1
150 pdbx_entity_branch_link.leaving_atom_id_1
151 pdbx_entity_branch_link.atom_stereo_config_1
152 pdbx_entity_branch_link.entity_branch_list_num_2
153 pdbx_entity_branch_link.comp_id_2
154 pdbx_entity_branch_link.atom_id_2
155 pdbx_entity_branch_link.leaving_atom_id_2
156 pdbx_entity_branch_link.atom_stereo_config_2
157 pdbx_entity_branch_link.value_order
158 pdbx_entity_branch_link.details
159 pdbx_branch_scheme.asym_id
160 pdbx_branch_scheme.entity_id
161 pdbx_branch_scheme.mon_id
162 pdbx_branch_scheme.num
163 pdbx_branch_scheme.auth_asym_id
164 pdbx_branch_scheme.auth_mon_id
165 pdbx_branch_scheme.auth_seq_num
166 pdbx_branch_scheme.hetero
167 pdbx_branch_scheme.pdb_mon_id
168 pdbx_branch_scheme.pdb_asym_id
169 pdbx_branch_scheme.pdb_seq_num
170 pdbx_entity_branch_descriptor.ordinal
171 pdbx_entity_branch_descriptor.entity_id
172 pdbx_entity_branch_descriptor.descriptor
173 pdbx_entity_branch_descriptor.type
174 pdbx_entity_branch_descriptor.program
175 pdbx_entity_branch_descriptor.program_version
176 pdbx_entity_nonpoly.entity_id
177 pdbx_entity_nonpoly.name
178 pdbx_entity_nonpoly.comp_id
179 pdbx_nonpoly_scheme.asym_id
180 pdbx_nonpoly_scheme.entity_id
181 pdbx_nonpoly_scheme.mon_id
182 pdbx_nonpoly_scheme.ndb_seq_num
183 pdbx_nonpoly_scheme.pdb_seq_num
184 pdbx_nonpoly_scheme.auth_seq_num
185 pdbx_nonpoly_scheme.pdb_mon_id
186 pdbx_nonpoly_scheme.auth_mon_id
187 pdbx_nonpoly_scheme.pdb_strand_id
188 pdbx_nonpoly_scheme.pdb_ins_code
189 entry.id
190 audit_conform.dict_name
191 audit_conform.dict_version
192 audit_conform.dict_location
193 database_2.database_id
194 database_2.database_code
195 audit_author.name
196 audit_author.pdbx_ordinal
197 audit_author.identifier_ORCID
198 citation.id
199 citation.title
200 citation.journal_abbrev
201 citation.journal_volume
202 citation.page_first
203 citation.page_last
204 citation.year
205 citation.journal_id_ASTM
206 citation.country
207 citation.journal_id_ISSN
208 citation.journal_id_CSD
209 citation.book_publisher
210 citation.pdbx_database_id_PubMed
211 citation.pdbx_database_id_DOI
212 citation_author.citation_id
213 citation_author.name
214 citation_author.ordinal
215 exptl.entry_id
216 exptl.method
217 struct.entry_id
218 struct.title
219 struct.pdbx_descriptor
220 struct_asym.id
221 struct_asym.pdbx_blank_PDB_chainid_flag
222 struct_asym.pdbx_modified
223 struct_asym.entity_id
224 struct_asym.details
225 struct_conf.conf_type_id
226 struct_conf.id
227 struct_conf.pdbx_PDB_helix_id
228 struct_conf.beg_label_comp_id
229 struct_conf.beg_label_asym_id
230 struct_conf.beg_label_seq_id
231 struct_conf.pdbx_beg_PDB_ins_code
232 struct_conf.end_label_comp_id
233 struct_conf.end_label_asym_id
234 struct_conf.end_label_seq_id
235 struct_conf.pdbx_end_PDB_ins_code
236 struct_conf.beg_auth_comp_id
237 struct_conf.beg_auth_asym_id
238 struct_conf.beg_auth_seq_id
239 struct_conf.end_auth_comp_id
240 struct_conf.end_auth_asym_id
241 struct_conf.end_auth_seq_id
242 struct_conf.pdbx_PDB_helix_class
243 struct_conf.details
244 struct_conf.pdbx_PDB_helix_length
245 struct_conn.id
246 struct_conn.conn_type_id
247 struct_conn.pdbx_PDB_id
248 struct_conn.ptnr1_label_asym_id
249 struct_conn.ptnr1_label_comp_id
250 struct_conn.ptnr1_label_seq_id
251 struct_conn.ptnr1_label_atom_id
252 struct_conn.pdbx_ptnr1_label_alt_id
253 struct_conn.pdbx_ptnr1_PDB_ins_code
254 struct_conn.pdbx_ptnr1_standard_comp_id
255 struct_conn.ptnr1_symmetry
256 struct_conn.ptnr2_label_asym_id
257 struct_conn.ptnr2_label_comp_id
258 struct_conn.ptnr2_label_seq_id
259 struct_conn.ptnr2_label_atom_id
260 struct_conn.pdbx_ptnr2_label_alt_id
261 struct_conn.pdbx_ptnr2_PDB_ins_code
262 struct_conn.ptnr1_auth_asym_id
263 struct_conn.ptnr1_auth_comp_id
264 struct_conn.ptnr1_auth_seq_id
265 struct_conn.ptnr2_auth_asym_id
266 struct_conn.ptnr2_auth_comp_id
267 struct_conn.ptnr2_auth_seq_id
268 struct_conn.ptnr2_symmetry
269 struct_conn.pdbx_ptnr3_label_atom_id
270 struct_conn.pdbx_ptnr3_label_seq_id
271 struct_conn.pdbx_ptnr3_label_comp_id
272 struct_conn.pdbx_ptnr3_label_asym_id
273 struct_conn.pdbx_ptnr3_label_alt_id
274 struct_conn.pdbx_ptnr3_PDB_ins_code
275 struct_conn.details
276 struct_conn.pdbx_dist_value
277 struct_conn.pdbx_value_order
278 struct_conn_type.id
279 struct_conn_type.criteria
280 struct_conn_type.reference
281 struct_keywords.entry_id
282 struct_keywords.pdbx_keywords
283 struct_keywords.text
284 struct_ncs_oper.id
285 struct_ncs_oper.code
286 struct_ncs_oper.matrix
287 struct_ncs_oper.vector
288 struct_ncs_oper.details
289 struct_sheet_range.sheet_id
290 struct_sheet_range.id
291 struct_sheet_range.beg_label_comp_id
292 struct_sheet_range.beg_label_asym_id
293 struct_sheet_range.beg_label_seq_id
294 struct_sheet_range.pdbx_beg_PDB_ins_code
295 struct_sheet_range.end_label_comp_id
296 struct_sheet_range.end_label_asym_id
297 struct_sheet_range.end_label_seq_id
298 struct_sheet_range.pdbx_end_PDB_ins_code
299 struct_sheet_range.beg_auth_comp_id
300 struct_sheet_range.beg_auth_asym_id
301 struct_sheet_range.beg_auth_seq_id
302 struct_sheet_range.end_auth_comp_id
303 struct_sheet_range.end_auth_asym_id
304 struct_sheet_range.end_auth_seq_id
305 struct_site.id
306 struct_site.pdbx_evidence_code
307 struct_site.pdbx_auth_asym_id
308 struct_site.pdbx_auth_comp_id
309 struct_site.pdbx_auth_seq_id
310 struct_site.pdbx_auth_ins_code
311 struct_site.pdbx_num_residues
312 struct_site.details
313 struct_site_gen.id
314 struct_site_gen.site_id
315 struct_site_gen.pdbx_num_res
316 struct_site_gen.label_comp_id
317 struct_site_gen.label_asym_id
318 struct_site_gen.label_seq_id
319 struct_site_gen.pdbx_auth_ins_code
320 struct_site_gen.auth_comp_id
321 struct_site_gen.auth_asym_id
322 struct_site_gen.auth_seq_id
323 struct_site_gen.label_atom_id
324 struct_site_gen.label_alt_id
325 struct_site_gen.symmetry
326 struct_site_gen.details
327 symmetry.entry_id
328 symmetry.cell_setting
329 symmetry.Int_Tables_number
330 symmetry.space_group_name_Hall
331 symmetry.space_group_name_H-M
332 pdbx_molecule.instance_id
333 pdbx_molecule.prd_id
334 pdbx_molecule.asym_id
335 pdbx_molecule_features.prd_id
336 pdbx_molecule_features.name
337 pdbx_molecule_features.type
338 pdbx_molecule_features.class
339 pdbx_molecule_features.details
340 pdbx_reference_entity_link.prd_id
341 pdbx_reference_entity_link.link_id
342 pdbx_reference_entity_link.link_class
343 pdbx_reference_entity_link.ref_entity_id_1
344 pdbx_reference_entity_link.entity_seq_num_1
345 pdbx_reference_entity_link.comp_id_1
346 pdbx_reference_entity_link.atom_id_1
347 pdbx_reference_entity_link.ref_entity_id_2
348 pdbx_reference_entity_link.entity_seq_num_2
349 pdbx_reference_entity_link.comp_id_2
350 pdbx_reference_entity_link.atom_id_2
351 pdbx_reference_entity_link.value_order
352 pdbx_reference_entity_link.component_1
353 pdbx_reference_entity_link.component_2
354 pdbx_reference_entity_link.details
355 pdbx_reference_entity_list.prd_id
356 pdbx_reference_entity_list.ref_entity_id
357 pdbx_reference_entity_list.component_id
358 pdbx_reference_entity_list.type
359 pdbx_reference_entity_list.details
360 pdbx_reference_entity_poly_link.prd_id
361 pdbx_reference_entity_poly_link.ref_entity_id
362 pdbx_reference_entity_poly_link.link_id
363 pdbx_reference_entity_poly_link.atom_id_1
364 pdbx_reference_entity_poly_link.comp_id_1
365 pdbx_reference_entity_poly_link.entity_seq_num_1
366 pdbx_reference_entity_poly_link.atom_id_2
367 pdbx_reference_entity_poly_link.comp_id_2
368 pdbx_reference_entity_poly_link.entity_seq_num_2
369 pdbx_reference_entity_poly_link.value_order
370 pdbx_reference_entity_poly_link.component_id
371 pdbx_struct_assembly.id
372 pdbx_struct_assembly.details
373 pdbx_struct_assembly.method_details
374 pdbx_struct_assembly.oligomeric_details
375 pdbx_struct_assembly.oligomeric_count
376 pdbx_struct_assembly_gen.assembly_id
377 pdbx_struct_assembly_gen.oper_expression
378 pdbx_struct_assembly_gen.asym_id_list
379 pdbx_struct_oper_list.id
380 pdbx_struct_oper_list.type
381 pdbx_struct_oper_list.name
382 pdbx_struct_oper_list.symmetry_operation
383 pdbx_struct_oper_list.matrix
384 pdbx_struct_oper_list.vector
385 pdbx_struct_mod_residue.id
386 pdbx_struct_mod_residue.label_asym_id
387 pdbx_struct_mod_residue.label_seq_id
388 pdbx_struct_mod_residue.label_comp_id
389 pdbx_struct_mod_residue.auth_asym_id
390 pdbx_struct_mod_residue.auth_seq_id
391 pdbx_struct_mod_residue.auth_comp_id
392 pdbx_struct_mod_residue.PDB_ins_code
393 pdbx_struct_mod_residue.parent_comp_id
394 pdbx_struct_mod_residue.details
395 pdbx_unobs_or_zero_occ_residues.id
396 pdbx_unobs_or_zero_occ_residues.PDB_model_num
397 pdbx_unobs_or_zero_occ_residues.polymer_flag
398 pdbx_unobs_or_zero_occ_residues.occupancy_flag
399 pdbx_unobs_or_zero_occ_residues.auth_asym_id
400 pdbx_unobs_or_zero_occ_residues.auth_comp_id
401 pdbx_unobs_or_zero_occ_residues.auth_seq_id
402 pdbx_unobs_or_zero_occ_residues.PDB_ins_code
403 pdbx_unobs_or_zero_occ_residues.label_asym_id
404 pdbx_unobs_or_zero_occ_residues.label_comp_id
405 pdbx_unobs_or_zero_occ_residues.label_seq_id
406 ihm_struct_assembly.id
407 ihm_struct_assembly.name
408 ihm_struct_assembly.description
409 ihm_struct_assembly_details.id
410 ihm_struct_assembly_details.assembly_id
411 ihm_struct_assembly_details.parent_assembly_id
412 ihm_struct_assembly_details.entity_description
413 ihm_struct_assembly_details.entity_id
414 ihm_struct_assembly_details.asym_id
415 ihm_struct_assembly_details.entity_poly_segment_id
416 ihm_model_representation.id
417 ihm_model_representation.name
418 ihm_model_representation.details
419 ihm_model_representation_details.id
420 ihm_model_representation_details.representation_id
421 ihm_model_representation_details.entity_id
422 ihm_model_representation_details.entity_description
423 ihm_model_representation_details.entity_asym_id
424 ihm_model_representation_details.entity_poly_segment_id
425 ihm_model_representation_details.model_object_primitive
426 ihm_model_representation_details.starting_model_id
427 ihm_model_representation_details.model_mode
428 ihm_model_representation_details.model_granularity
429 ihm_model_representation_details.model_object_count
430 ihm_external_reference_info.reference_id
431 ihm_external_reference_info.reference_provider
432 ihm_external_reference_info.reference_type
433 ihm_external_reference_info.reference
434 ihm_external_reference_info.refers_to
435 ihm_external_reference_info.associated_url
436 ihm_external_files.id
437 ihm_external_files.reference_id
438 ihm_external_files.file_path
439 ihm_external_files.content_type
440 ihm_external_files.file_size_bytes
441 ihm_external_files.details
442 ihm_dataset_list.id
443 ihm_dataset_list.data_type
444 ihm_dataset_list.database_hosted
445 ihm_dataset_group.id
446 ihm_dataset_group.name
447 ihm_dataset_group.application
448 ihm_dataset_group.details
449 ihm_dataset_group_link.group_id
450 ihm_dataset_group_link.dataset_list_id
451 ihm_dataset_external_reference.id
452 ihm_dataset_external_reference.dataset_list_id
453 ihm_dataset_external_reference.file_id
454 ihm_dataset_related_db_reference.id
455 ihm_dataset_related_db_reference.dataset_list_id
456 ihm_dataset_related_db_reference.db_name
457 ihm_dataset_related_db_reference.accession_code
458 ihm_dataset_related_db_reference.version
459 ihm_dataset_related_db_reference.details
460 ihm_related_datasets.dataset_list_id_derived
461 ihm_related_datasets.dataset_list_id_primary
462 ihm_poly_residue_feature.ordinal_id
463 ihm_poly_residue_feature.feature_id
464 ihm_poly_residue_feature.entity_id
465 ihm_poly_residue_feature.asym_id
466 ihm_poly_residue_feature.seq_id_begin
467 ihm_poly_residue_feature.comp_id_begin
468 ihm_poly_residue_feature.seq_id_end
469 ihm_poly_residue_feature.comp_id_end
470 ihm_feature_list.feature_id
471 ihm_feature_list.feature_type
472 ihm_feature_list.entity_type
473 ihm_cross_link_list.id
474 ihm_cross_link_list.group_id
475 ihm_cross_link_list.entity_description_1
476 ihm_cross_link_list.entity_id_1
477 ihm_cross_link_list.seq_id_1
478 ihm_cross_link_list.comp_id_1
479 ihm_cross_link_list.entity_description_2
480 ihm_cross_link_list.entity_id_2
481 ihm_cross_link_list.seq_id_2
482 ihm_cross_link_list.comp_id_2
483 ihm_cross_link_list.linker_type
484 ihm_cross_link_list.dataset_list_id
485 ihm_cross_link_restraint.id
486 ihm_cross_link_restraint.group_id
487 ihm_cross_link_restraint.entity_id_1
488 ihm_cross_link_restraint.asym_id_1
489 ihm_cross_link_restraint.seq_id_1
490 ihm_cross_link_restraint.atom_id_1
491 ihm_cross_link_restraint.comp_id_1
492 ihm_cross_link_restraint.entity_id_2
493 ihm_cross_link_restraint.asym_id_2
494 ihm_cross_link_restraint.seq_id_2
495 ihm_cross_link_restraint.atom_id_2
496 ihm_cross_link_restraint.comp_id_2
497 ihm_cross_link_restraint.restraint_type
498 ihm_cross_link_restraint.conditional_crosslink_flag
499 ihm_cross_link_restraint.model_granularity
500 ihm_cross_link_restraint.distance_threshold
501 ihm_cross_link_restraint.psi
502 ihm_cross_link_restraint.sigma_1
503 ihm_cross_link_restraint.sigma_2
504 ihm_cross_link_result_parameters.id
505 ihm_cross_link_result_parameters.restraint_id
506 ihm_cross_link_result_parameters.model_id
507 ihm_cross_link_result_parameters.psi
508 ihm_cross_link_result_parameters.sigma_1
509 ihm_cross_link_result_parameters.sigma_2
510 ihm_sas_restraint.id
511 ihm_sas_restraint.dataset_list_id
512 ihm_sas_restraint.model_id
513 ihm_sas_restraint.struct_assembly_id
514 ihm_sas_restraint.profile_segment_flag
515 ihm_sas_restraint.fitting_atom_type
516 ihm_sas_restraint.fitting_method
517 ihm_sas_restraint.fitting_state
518 ihm_sas_restraint.radius_of_gyration
519 ihm_sas_restraint.chi_value
520 ihm_sas_restraint.details
521 ihm_derived_distance_restraint.id
522 ihm_derived_distance_restraint.group_id
523 ihm_derived_distance_restraint.feature_id_1
524 ihm_derived_distance_restraint.feature_id_2
525 ihm_derived_distance_restraint.group_conditionality
526 ihm_derived_distance_restraint.restraint_type
527 ihm_derived_distance_restraint.distance_upper_limit
528 ihm_derived_distance_restraint.random_exclusion_fraction
529 ihm_derived_distance_restraint.dataset_list_id
530 ihm_2dem_class_average_restraint.id
531 ihm_2dem_class_average_restraint.dataset_list_id
532 ihm_2dem_class_average_restraint.number_raw_micrographs
533 ihm_2dem_class_average_restraint.pixel_size_width
534 ihm_2dem_class_average_restraint.pixel_size_height
535 ihm_2dem_class_average_restraint.image_resolution
536 ihm_2dem_class_average_restraint.image_segment_flag
537 ihm_2dem_class_average_restraint.number_of_projections
538 ihm_2dem_class_average_restraint.struct_assembly_id
539 ihm_2dem_class_average_restraint.details
540 ihm_2dem_class_average_fitting.id
541 ihm_2dem_class_average_fitting.restraint_id
542 ihm_2dem_class_average_fitting.model_id
543 ihm_2dem_class_average_fitting.cross_correlation_coefficient
544 ihm_2dem_class_average_fitting.rot_matrix
545 ihm_2dem_class_average_fitting.tr_vector
546 ihm_3dem_restraint.id
547 ihm_3dem_restraint.dataset_list_id
548 ihm_3dem_restraint.fitting_method
549 ihm_3dem_restraint.struct_assembly_id
550 ihm_3dem_restraint.number_of_gaussians
551 ihm_3dem_restraint.model_id
552 ihm_3dem_restraint.cross_correlation_coefficient
553 ihm_predicted_contact_restraint.id
554 ihm_predicted_contact_restraint.group_id
555 ihm_predicted_contact_restraint.entity_id_1
556 ihm_predicted_contact_restraint.asym_id_1
557 ihm_predicted_contact_restraint.seq_id_1
558 ihm_predicted_contact_restraint.comp_id_1
559 ihm_predicted_contact_restraint.rep_atom_1
560 ihm_predicted_contact_restraint.entity_id_2
561 ihm_predicted_contact_restraint.asym_id_2
562 ihm_predicted_contact_restraint.seq_id_2
563 ihm_predicted_contact_restraint.comp_id_2
564 ihm_predicted_contact_restraint.rep_atom_2
565 ihm_predicted_contact_restraint.restraint_type
566 ihm_predicted_contact_restraint.distance_lower_limit
567 ihm_predicted_contact_restraint.distance_upper_limit
568 ihm_predicted_contact_restraint.probability
569 ihm_predicted_contact_restraint.model_granularity
570 ihm_predicted_contact_restraint.dataset_list_id
571 ihm_predicted_contact_restraint.software_id
572 ihm_starting_model_details.starting_model_id
573 ihm_starting_model_details.entity_id
574 ihm_starting_model_details.entity_description
575 ihm_starting_model_details.asym_id
576 ihm_starting_model_details.entity_poly_segment_id
577 ihm_starting_model_details.starting_model_source
578 ihm_starting_model_details.starting_model_auth_asym_id
579 ihm_starting_model_details.starting_model_sequence_offset
580 ihm_starting_model_details.dataset_list_id
581 ihm_starting_comparative_models.id
582 ihm_starting_comparative_models.starting_model_id
583 ihm_starting_comparative_models.starting_model_auth_asym_id
584 ihm_starting_comparative_models.starting_model_seq_id_begin
585 ihm_starting_comparative_models.starting_model_seq_id_end
586 ihm_starting_comparative_models.template_auth_asym_id
587 ihm_starting_comparative_models.template_seq_id_begin
588 ihm_starting_comparative_models.template_seq_id_end
589 ihm_starting_comparative_models.template_sequence_identity
590 ihm_starting_comparative_models.template_sequence_identity_denominator
591 ihm_starting_comparative_models.template_dataset_list_id
592 ihm_starting_comparative_models.alignment_file_id
593 ihm_starting_model_coord.starting_model_id
594 ihm_starting_model_coord.group_PDB
595 ihm_starting_model_coord.id
596 ihm_starting_model_coord.type_symbol
597 ihm_starting_model_coord.atom_id
598 ihm_starting_model_coord.comp_id
599 ihm_starting_model_coord.entity_id
600 ihm_starting_model_coord.asym_id
601 ihm_starting_model_coord.seq_id
602 ihm_starting_model_coord.Cartn_x
603 ihm_starting_model_coord.Cartn_y
604 ihm_starting_model_coord.Cartn_z
605 ihm_starting_model_coord.B_iso_or_equiv
606 ihm_starting_model_coord.ordinal_id
607 ihm_starting_model_seq_dif.id
608 ihm_starting_model_seq_dif.entity_id
609 ihm_starting_model_seq_dif.asym_id
610 ihm_starting_model_seq_dif.seq_id
611 ihm_starting_model_seq_dif.comp_id
612 ihm_starting_model_seq_dif.starting_model_id
613 ihm_starting_model_seq_dif.db_asym_id
614 ihm_starting_model_seq_dif.db_seq_id
615 ihm_starting_model_seq_dif.db_comp_id
616 ihm_starting_model_seq_dif.details
617 ihm_modeling_protocol.id
618 ihm_modeling_protocol.protocol_name
619 ihm_modeling_protocol.num_steps
620 ihm_modeling_protocol_details.id
621 ihm_modeling_protocol_details.protocol_id
622 ihm_modeling_protocol_details.step_id
623 ihm_modeling_protocol_details.struct_assembly_id
624 ihm_modeling_protocol_details.dataset_group_id
625 ihm_modeling_protocol_details.struct_assembly_description
626 ihm_modeling_protocol_details.step_name
627 ihm_modeling_protocol_details.step_method
628 ihm_modeling_protocol_details.num_models_begin
629 ihm_modeling_protocol_details.num_models_end
630 ihm_modeling_protocol_details.multi_scale_flag
631 ihm_modeling_protocol_details.multi_state_flag
632 ihm_modeling_protocol_details.ordered_flag
633 ihm_modeling_protocol_details.software_id
634 ihm_modeling_protocol_details.script_file_id
635 ihm_modeling_post_process.id
636 ihm_modeling_post_process.protocol_id
637 ihm_modeling_post_process.analysis_id
638 ihm_modeling_post_process.step_id
639 ihm_modeling_post_process.type
640 ihm_modeling_post_process.feature
641 ihm_modeling_post_process.num_models_begin
642 ihm_modeling_post_process.num_models_end
643 ihm_ensemble_info.ensemble_id
644 ihm_ensemble_info.ensemble_name
645 ihm_ensemble_info.post_process_id
646 ihm_ensemble_info.model_group_id
647 ihm_ensemble_info.ensemble_clustering_method
648 ihm_ensemble_info.ensemble_clustering_feature
649 ihm_ensemble_info.num_ensemble_models
650 ihm_ensemble_info.num_ensemble_models_deposited
651 ihm_ensemble_info.ensemble_precision_value
652 ihm_ensemble_info.ensemble_file_id
653 ihm_localization_density_files.id
654 ihm_localization_density_files.file_id
655 ihm_localization_density_files.ensemble_id
656 ihm_localization_density_files.entity_id
657 ihm_localization_density_files.asym_id
658 ihm_localization_density_files.entity_poly_segment_id
659 ihm_model_list.model_id
660 ihm_model_list.model_name
661 ihm_model_list.assembly_id
662 ihm_model_list.protocol_id
663 ihm_model_list.representation_id
664 ihm_model_group.id
665 ihm_model_group.name
666 ihm_model_group.details
667 ihm_model_group_link.group_id
668 ihm_model_group_link.model_id
669 ihm_model_representative.id
670 ihm_model_representative.model_group_id
671 ihm_model_representative.model_id
672 ihm_model_representative.selection_criteria
673 ihm_sphere_obj_site.id
674 ihm_sphere_obj_site.entity_id
675 ihm_sphere_obj_site.seq_id_begin
676 ihm_sphere_obj_site.seq_id_end
677 ihm_sphere_obj_site.asym_id
678 ihm_sphere_obj_site.Cartn_x
679 ihm_sphere_obj_site.Cartn_y
680 ihm_sphere_obj_site.Cartn_z
681 ihm_sphere_obj_site.object_radius
682 ihm_sphere_obj_site.rmsf
683 ihm_sphere_obj_site.model_id
684 ihm_gaussian_obj_site.id
685 ihm_gaussian_obj_site.entity_id
686 ihm_gaussian_obj_site.seq_id_begin
687 ihm_gaussian_obj_site.seq_id_end
688 ihm_gaussian_obj_site.asym_id
689 ihm_gaussian_obj_site.mean_Cartn_x
690 ihm_gaussian_obj_site.mean_Cartn_y
691 ihm_gaussian_obj_site.mean_Cartn_z
692 ihm_gaussian_obj_site.weight
693 ihm_gaussian_obj_site.covariance_matrix
694 ihm_gaussian_obj_site.model_id
695 ihm_gaussian_obj_ensemble.id
696 ihm_gaussian_obj_ensemble.entity_id
697 ihm_gaussian_obj_ensemble.seq_id_begin
698 ihm_gaussian_obj_ensemble.seq_id_end
699 ihm_gaussian_obj_ensemble.asym_id
700 ihm_gaussian_obj_ensemble.mean_Cartn_x
701 ihm_gaussian_obj_ensemble.mean_Cartn_y
702 ihm_gaussian_obj_ensemble.mean_Cartn_z
703 ihm_gaussian_obj_ensemble.weight
704 ihm_gaussian_obj_ensemble.covariance_matrix
705 ihm_gaussian_obj_ensemble.ensemble_id
706 ihm_multi_state_modeling.state_id
707 ihm_multi_state_modeling.state_group_id
708 ihm_multi_state_modeling.population_fraction
709 ihm_multi_state_modeling.population_fraction_sd
710 ihm_multi_state_modeling.state_type
711 ihm_multi_state_modeling.state_name
712 ihm_multi_state_modeling.experiment_type
713 ihm_multi_state_modeling.details

View File

@@ -0,0 +1,76 @@
cell.length_a
cell.length_b
cell.length_c
cell.angle_alpha
cell.angle_beta
cell.angle_gamma
symmetry.space_group_name_H-M
entry.id
struct.title
pdbx_database_status.recvd_initial_deposition_date
pdbx_audit_revision_history.revision_date
struct_ncs_oper
pdbx_struct_assembly_gen
pdbx_struct_oper_list
entity.id
entity.type
entity.pdbx_description
entity_poly.entity_id
entity_poly.pdbx_seq_one_letter_code
entity_poly.pdbx_strand_id
exptl.method
refine.ls_d_res_low
refine.ls_R_factor_R_free
refine.ls_R_factor_R_work
atom_site.pdbx_formal_charge
atom_site.label_atom_id
atom_site.type_symbol
chem_comp.id
chem_comp.type
chem_comp.name
chem_comp_bond
atom_site.Cartn_x
atom_site.Cartn_y
atom_site.Cartn_z
atom_site.B_iso_or_equiv
atom_site.id
atom_site.label_alt_id
atom_site.occupancy
atom_site.label_seq_id
atom_site.label_comp_id
struct_sheet_range.id
struct_sheet_range.beg_label_asym_id
struct_sheet_range.beg_label_seq_id
struct_sheet_range.pdbx_beg_PDB_ins_code
struct_sheet_range.end_label_asym_id
struct_sheet_range.end_label_seq_id
struct_sheet_range.pdbx_end_PDB_ins_code
struct_conf.conf_type_id
struct_conf.id
struct_conf.beg_label_asym_id
struct_conf.beg_label_seq_id
struct_conf.pdbx_beg_PDB_ins_code
struct_conf.end_label_asym_id
struct_conf.end_label_seq_id
struct_conf.pdbx_end_PDB_ins_code
atom_site.pdbx_PDB_ins_code
atom_site.label_asym_id
atom_site.auth_asym_id
1 cell.length_a
2 cell.length_b
3 cell.length_c
4 cell.angle_alpha
5 cell.angle_beta
6 cell.angle_gamma
7 symmetry.space_group_name_H-M
8 entry.id
9 struct.title
10 pdbx_database_status.recvd_initial_deposition_date
11 pdbx_audit_revision_history.revision_date
12 struct_ncs_oper
13 pdbx_struct_assembly_gen
14 pdbx_struct_oper_list
15 entity.id
16 entity.type
17 entity.pdbx_description
18 entity_poly.entity_id
19 entity_poly.pdbx_seq_one_letter_code
20 entity_poly.pdbx_strand_id
21 exptl.method
22 refine.ls_d_res_low
23 refine.ls_R_factor_R_free
24 refine.ls_R_factor_R_work
25 atom_site.pdbx_formal_charge
26 atom_site.label_atom_id
27 atom_site.type_symbol
28 chem_comp.id
29 chem_comp.type
30 chem_comp.name
31 chem_comp_bond
32 atom_site.Cartn_x
33 atom_site.Cartn_y
34 atom_site.Cartn_z
35 atom_site.B_iso_or_equiv
36 atom_site.id
37 atom_site.label_alt_id
38 atom_site.occupancy
39 atom_site.label_seq_id
40 atom_site.label_comp_id
41 struct_sheet_range.id
42 struct_sheet_range.beg_label_asym_id
43 struct_sheet_range.beg_label_seq_id
44 struct_sheet_range.pdbx_beg_PDB_ins_code
45 struct_sheet_range.end_label_asym_id
46 struct_sheet_range.end_label_seq_id
47 struct_sheet_range.pdbx_end_PDB_ins_code
48 struct_conf.conf_type_id
49 struct_conf.id
50 struct_conf.beg_label_asym_id
51 struct_conf.beg_label_seq_id
52 struct_conf.pdbx_beg_PDB_ins_code
53 struct_conf.end_label_asym_id
54 struct_conf.end_label_seq_id
55 struct_conf.pdbx_end_PDB_ins_code
56 atom_site.pdbx_PDB_ins_code
57 atom_site.label_asym_id
58 atom_site.auth_asym_id

View File

@@ -54,12 +54,12 @@ Sometimes nodejs might run into problems with memory. This is usually resolved b
## Preprocessor
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/servers/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
## Local Mode
The server can be run in local/file based mode using ``node lib/servers/model/query`` (``model-server-query`` binary from the NPM package).
The server can be run in local/file based mode using ``node lib/servers/servers/model/query`` (``model-server-query`` binary from the NPM package).
Custom Properties
=================

View File

@@ -28,7 +28,7 @@ npm run build-tsc
and run the server by
```
node lib/servers/volume/server
node lib/servers/servers/volume/server
```
## From NPM
@@ -60,11 +60,11 @@ Sometimes nodejs might run into problems with memory. This is usually resolved b
## Preparing the Data
For the server to work, CCP4/MAP (models 0, 1, 2 are supported) input data need to be converted into a custom block format.
To achieve this, use the ``pack`` application (``node lib/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
To achieve this, use the ``pack`` application (``node lib/servers/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
## Local Mode
The program ``lib/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
The program ``lib/servers/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
## Navigating the Source Code

7001
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.6.2",
"version": "0.7.0-dev.2",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -11,25 +11,29 @@
"url": "https://github.com/molstar/molstar/issues"
},
"scripts": {
"lint": "eslint src/**/*.ts",
"lint": "eslint ./**/*.{ts,tsx}",
"lint-fix": "eslint ./**/*.{ts,tsx} --fix",
"test": "npm run lint && jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"build-tsc": "tsc --incremental",
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
"build-webpack": "webpack --mode production",
"watch": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "tsc --incremental && tsc --build tsconfig.servers.json --incremental",
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
"build-webpack": "webpack --mode production --config ./webpack.config.production.js",
"build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
"watch": "concurrently -c \"green,green,gray,gray\" --names \"tsc,srv,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-servers\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
"watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
"watch-tsc": "tsc --watch --incremental",
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
"watch-servers": "tsc --build tsconfig.servers.json --watch --incremental",
"watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
"watch-webpack": "webpack -w --mode development --display minimal",
"watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js",
"watch-webpack-viewer-debug": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.debug.js",
"serve": "http-server -p 1338",
"model-server": "node lib/servers/model/server.js",
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
"volume-server-test": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
"plugin-state": "node lib/servers/plugin-state/index.js",
"model-server": "node lib/servers/servers/model/server.js",
"model-server-watch": "nodemon --watch lib lib/servers/servers/model/server.js",
"volume-server-test": "node lib/servers/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
"plugin-state": "node lib/servers/servers/plugin-state/index.js",
"preversion": "npm run test",
"postversion": "git push && git push --tags",
"prepublishOnly": "npm run test && npm run build"
@@ -38,12 +42,14 @@
"lib/"
],
"bin": {
"model-server": "lib/servers/model/server.js",
"model-server-query": "lib/servers/model/local.js",
"model-server-preprocess": "lib/servers/model/preprocess.js",
"volume-server": "lib/servers/volume/server.js",
"volume-server-query": "lib/servers/volume/query.js",
"volume-server-pack": "lib/servers/volume/pack.js"
"cif2bcif": "lib/apps/cif2bcif/index.js",
"cifschema": "lib/apps/cifschema/index.js",
"model-server": "lib/servers/servers/model/server.js",
"model-server-query": "lib/servers/servers/model/query.js",
"model-server-preprocess": "lib/servers/servers/model/preprocess.js",
"volume-server": "lib/servers/servers/volume/server.js",
"volume-server-query": "lib/servers/servers/volume/query.js",
"volume-server-pack": "lib/servers/servers/volume/pack.js"
},
"nodemonConfig": {
"ignoreRoot": [
@@ -76,50 +82,52 @@
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^1.13.1",
"@graphql-codegen/cli": "^1.13.1",
"@graphql-codegen/time": "^1.13.1",
"@graphql-codegen/typescript": "^1.13.1",
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.1",
"@graphql-codegen/typescript-graphql-request": "^1.13.1",
"@graphql-codegen/typescript-operations": "^1.13.1",
"@graphql-codegen/add": "^1.13.2",
"@graphql-codegen/cli": "^1.13.2",
"@graphql-codegen/time": "^1.13.2",
"@graphql-codegen/typescript": "^1.13.2",
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.2",
"@graphql-codegen/typescript-graphql-request": "^1.13.2",
"@graphql-codegen/typescript-operations": "^1.13.2",
"@types/cors": "^2.8.6",
"@typescript-eslint/eslint-plugin": "^2.26.0",
"@typescript-eslint/parser": "^2.26.0",
"@typescript-eslint/eslint-plugin": "^2.28.0",
"@typescript-eslint/parser": "^2.28.0",
"benchmark": "^2.1.4",
"circular-dependency-plugin": "^5.2.0",
"concurrently": "^5.1.0",
"cpx2": "^2.0.0",
"css-loader": "^3.4.2",
"css-loader": "^3.5.2",
"eslint": "^6.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.0.0",
"fs-extra": "^9.0.0",
"graphql": "^15.0.0",
"http-server": "^0.12.1",
"jest": "^25.2.7",
"jest": "^25.3.0",
"jest-raw-loader": "^1.0.1",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"pascal-case": "^3.1.1",
"raw-loader": "^4.0.0",
"raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"simple-git": "^1.132.0",
"style-loader": "^1.1.3",
"ts-jest": "^25.3.1",
"style-loader": "^1.1.4",
"ts-jest": "^25.4.0",
"typescript": "^3.8.3",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@types/argparse": "^1.0.38",
"@types/benchmark": "^1.0.31",
"@types/compression": "1.7.0",
"@types/express": "^4.17.4",
"@types/express": "^4.17.6",
"@types/jest": "^25.2.1",
"@types/node": "^13.11.0",
"@types/node-fetch": "^2.5.5",
"@types/react": "^16.9.32",
"@types/node": "^13.13.0",
"@types/node-fetch": "^2.5.6",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"@types/swagger-ui-dist": "3.0.5",
"argparse": "^1.0.10",
@@ -127,7 +135,6 @@
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"graphql": "^14.6.0",
"immer": "^6.0.3",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",

View File

@@ -1,30 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import * as React from 'react';
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
export class BasicWrapperControls extends PluginUIComponent {
render() {
return <div style={{ overflowY: 'auto', display: 'block', height: '100%' }}>
<TransformUpdaterControl nodeRef='asm' />
<TransformUpdaterControl nodeRef='seq-visual' header={{ name: 'Sequence Visual' }} />
<TransformUpdaterControl nodeRef='het-visual' header={{ name: 'HET Visual' }} />
<TransformUpdaterControl nodeRef='water-visual' header={{ name: 'Water Visual' }} initiallyCollapsed={true} />
<TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
</div>;
}
}
export class CustomToastMessage extends PluginUIComponent {
render() {
return <>
Custom <i>Toast</i> content. No timeout.
</>;
}
}

View File

@@ -1,99 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { PluginContext } from '../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateBuilder } from '../../mol-state';
import Expression from '../../mol-script/language/expression';
import { ColorTheme } from '../../mol-theme/color';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
type SupportedFormats = 'cif' | 'pdb'
export namespace StateHelper {
export function download(b: StateBuilder.To<PSO.Root>, url: string, ref?: string) {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false }, { ref });
}
export function getModel(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, modelIndex = 0) {
const parsed = format === 'cif'
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex });
}
export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
};
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
const expression = MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
})
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Chain ${auth_asym_id}` });
}
export function select(b: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression) {
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression });
}
export function selectSurroundingsOfFirstResidue(b: StateBuilder.To<PSO.Molecule.Structure>, comp_id: string, radius: number) {
const expression = MS.struct.modifier.includeSurroundings({
0: MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
})
]),
radius
})
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Surr. ${comp_id} (${radius} ang)` });
}
export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) {
return b.apply(StateTransforms.Model.TransformStructureConformation,
{ transform: { name: 'components', params: { axis: Vec3.create(1, 0, 0), angle: 0, translation: Vec3.zero() } } },
{ tags: 'transform' });
}
export function transform(b: StateBuilder.To<PSO.Molecule.Structure>, matrix: Mat4) {
return b.apply(StateTransforms.Model.TransformStructureConformation, {
transform: { name: 'matrix', params: matrix }
}, { tags: 'transform' });
}
export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
const props = {
type: {
name: 'assembly' as const,
params: { id: id || 'deposited' }
}
}
return b.apply(StateTransforms.Model.StructureFromModel, props, { tags: 'asm' })
}
export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
return visualRoot;
}
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
visualRoot
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
return visualRoot;
}
}

View File

@@ -1,219 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { StateBuilder, StateTransform } from '../../mol-state';
import { StripedResidues } from './coloring';
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
import { CustomToastMessage } from './controls';
import { EmptyLoci } from '../../mol-model/loci';
import { StructureSelection } from '../../mol-model/structure';
import { Script } from '../../mol-script/script';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
class BasicWrapper {
plugin: PluginContext;
init(target: string | HTMLElement) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec,
layout: {
initial: {
isExpanded: false,
showControls: false
},
controls: {
// left: 'none',
// right: BasicWrapperControls
}
},
components: {
remoteState: 'none'
}
});
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
}
private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
const parsed = format === 'cif'
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
.apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
return visualRoot;
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
} else if (this.loadedParams.url === url) {
if (state.select('asm').length > 0) loadType = 'update';
}
let tree: StateBuilder.Root;
if (loadType === 'full') {
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
}
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset(this.plugin, { });
}
setBackground(color: number) {
const renderer = this.plugin.canvas3d!.props.renderer;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
}
animate = {
modelIndex: {
maxFPS: 8,
onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
stop: () => this.plugin.state.animation.stop()
}
}
coloring = {
applyStripes: async () => {
const state = this.plugin.state.data;
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
const tree = state.build();
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
for (const v of visuals) {
tree.to(v).update(old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update(this.plugin, { state, tree });
}
}
interactivity = {
highlightOn: () => {
const seq_id = 7;
const data = (this.plugin.state.data.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
'group-by': Q.struct.atomProperty.macromolecular.residueKey()
}), data);
const loci = StructureSelection.toLociWithSourceUnits(sel);
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
},
clearHighlight: () => {
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
tests = {
staticSuperposition: async () => {
const state = this.plugin.state.data;
const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
await PluginCommands.State.Update(this.plugin, { state, tree });
},
dynamicSuperposition: async () => {
await PluginCommands.State.RemoveObject(this.plugin, { state: this.plugin.state.data, ref: StateTransform.RootRef });
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
},
toggleValidationTooltip: async () => {
const state = this.plugin.state.behaviors;
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
await PluginCommands.State.Update(this.plugin, { state, tree });
},
showToasts: () => {
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 1',
message: 'This is an example text, timeout 3s',
key: 'toast-1',
timeoutMs: 3000
});
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 2',
message: CustomToastMessage,
key: 'toast-2'
});
},
hideToasts: () => {
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
}
}
}
(window as any).BasicMolStarWrapper = new BasicWrapper();

View File

@@ -1,108 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// TODO: move to an "example"
import { PluginContext } from '../../mol-plugin/context';
import { Mat4 } from '../../mol-math/linear-algebra';
import { StateHelper } from './helpers';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateSelection, StateBuilder } from '../../mol-state';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { compile } from '../../mol-script/runtime/query/compiler';
import { StructureSelection, QueryContext } from '../../mol-model/structure';
import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
import Expression from '../../mol-script/language/expression';
export type SuperpositionTestInput = {
pdbId: string,
auth_asym_id: string,
matrix: Mat4
}[];
// function getAxisAngleTranslation(m: Mat4) {
// const translation = Mat4.getTranslation(Vec3.zero(), m);
// const axis = Vec3.zero();
// const angle = 180 / Math.PI * Quat.getAxisAngle(axis, Mat4.getRotation(Quat.zero(), m));
// return { translation, axis, angle };
// }
export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) {
const b = ctx.state.data.build().toRoot();
for (const s of src) {
StateHelper.visual(ctx,
StateHelper.transform(
StateHelper.selectChain(
StateHelper.structure(
StateHelper.getModel(StateHelper.download(b, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`), 'cif')),
s.auth_asym_id
),
s.matrix
)
);
}
return b;
}
export const StaticSuperpositionTestData: SuperpositionTestInput = [
{ pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() },
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
[0.406, 0.879, 0.248, -200.633],
[0.693, -0.473, 0.544, 73.403],
[0.596, -0.049, -0.802, -14.209],
[0, 0, 0, 1]] )},
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
[-0.053, -0.077, 0.996, -45.633],
[-0.312, 0.949, 0.057, -12.255],
[-0.949, -0.307, -0.074, 53.562],
[0, 0, 0, 1]] )}
];
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
const state = ctx.state.data;
const structures = state.build().toRoot();
for (const s of src) {
StateHelper.structure(
StateHelper.getModel(StateHelper.download(structures, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`), 'cif'));
}
await PluginCommands.State.Update(ctx, { state, tree: structures });
const pivot = MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
})
]);
const rest = MS.struct.modifier.exceptBy({
0: MS.struct.generator.all(),
by: pivot
});
const query = compile<StructureSelection>(pivot);
const xs = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.obj!.data))));
const transforms = superposeStructures(selections);
const visuals = state.build();
siteVisual(ctx, StateHelper.selectSurroundingsOfFirstResidue(visuals.to(xs[0].transform.ref), 'HEM', 7), pivot, rest);
for (let i = 1; i < selections.length; i++) {
const root = visuals.to(xs[i].transform.ref);
siteVisual(ctx,
StateHelper.transform(StateHelper.selectSurroundingsOfFirstResidue(root, 'HEM', 7), transforms[i - 1].bTransform),
pivot, rest);
}
await PluginCommands.State.Update(ctx, { state, tree: visuals });
}
function siteVisual(ctx: PluginContext, b: StateBuilder.To<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
StateHelper.ballsAndSticks(ctx, b, pivot, 'residue-name');
StateHelper.ballsAndSticks(ctx, b, rest, 'uniform');
}

View File

@@ -4,57 +4,57 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse'
import * as util from 'util'
import * as path from 'path'
import * as fs from 'fs'
import * as zlib from 'zlib'
import fetch from 'node-fetch'
require('util.promisify').shim()
const readFile = util.promisify(fs.readFile)
const writeFile = util.promisify(fs.writeFile)
import * as argparse from 'argparse';
import * as util from 'util';
import * as path from 'path';
import * as fs from 'fs';
import * as zlib from 'zlib';
import fetch from 'node-fetch';
require('util.promisify').shim();
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
import { Progress } from '../../mol-task'
import { Database, Table, DatabaseCollection } from '../../mol-data/db'
import { CIF } from '../../mol-io/reader/cif'
import { CifWriter } from '../../mol-io/writer/cif'
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd'
import { SetUtils } from '../../mol-util/set'
import { DefaultMap } from '../../mol-util/map'
import { Progress } from '../../mol-task';
import { Database, Table, DatabaseCollection } from '../../mol-data/db';
import { CIF } from '../../mol-io/reader/cif';
import { CifWriter } from '../../mol-io/writer/cif';
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
import { SetUtils } from '../../mol-util/set';
import { DefaultMap } from '../../mol-util/map';
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
export async function ensureAvailable(path: string, url: string) {
if (FORCE_DOWNLOAD || !fs.existsSync(path)) {
console.log(`downloading ${url}...`)
const data = await fetch(url)
console.log(`downloading ${url}...`);
const data = await fetch(url);
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR);
}
if (url.endsWith('.gz')) {
await writeFile(path, zlib.gunzipSync(await data.buffer()))
await writeFile(path, zlib.gunzipSync(await data.buffer()));
} else {
await writeFile(path, await data.text())
await writeFile(path, await data.text());
}
console.log(`done downloading ${url}`)
console.log(`done downloading ${url}`);
}
}
export async function ensureDataAvailable() {
await ensureAvailable(CCD_PATH, CCD_URL)
await ensureAvailable(PVCD_PATH, PVCD_URL)
await ensureAvailable(CCD_PATH, CCD_URL);
await ensureAvailable(PVCD_PATH, PVCD_URL);
}
export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
const parsed = await parseCif(await readFile(path, 'utf8'))
return CIF.toDatabaseCollection(schema, parsed.result)
const parsed = await parseCif(await readFile(path, 'utf8'));
return CIF.toDatabaseCollection(schema, parsed.result);
}
export async function readCCD() {
return readFileAsCollection(CCD_PATH, CCD_Schema)
return readFileAsCollection(CCD_PATH, CCD_Schema);
}
export async function readPVCD() {
return readFileAsCollection(PVCD_PATH, CCD_Schema)
return readFileAsCollection(PVCD_PATH, CCD_Schema);
}
async function parseCif(data: string | Uint8Array) {
@@ -63,12 +63,12 @@ async function parseCif(data: string | Uint8Array) {
const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
console.timeEnd('parse cif');
if (parsed.isError) throw parsed;
return parsed
return parsed;
}
export function getEncodedCif(name: string, database: Database<Database.Schema>, binary = false) {
const encoder = CifWriter.createEncoder({ binary, encoderName: 'mol*' });
CifWriter.Encoder.writeDatabase(encoder, name, database)
CifWriter.Encoder.writeDatabase(encoder, name, database);
return encoder.getData();
}
@@ -76,58 +76,58 @@ type CCB = Table<CCD_Schema['chem_comp_bond']>
type CCA = Table<CCD_Schema['chem_comp_atom']>
function ccbKey(compId: string, atomId1: string, atomId2: string) {
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`;
}
function addChemCompBondToSet(set: Set<string>, ccb: CCB) {
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
set.add(ccbKey(ccb.comp_id.value(i), ccb.atom_id_1.value(i), ccb.atom_id_2.value(i)))
set.add(ccbKey(ccb.comp_id.value(i), ccb.atom_id_1.value(i), ccb.atom_id_2.value(i)));
}
return set
return set;
}
function addChemCompAtomToSet(set: Set<string>, cca: CCA) {
for (let i = 0, il = cca._rowCount; i < il; ++i) {
set.add(cca.atom_id.value(i))
set.add(cca.atom_id.value(i));
}
return set
return set;
}
function checkAddingBondsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
const ccbSetByParent = DefaultMap<string, Set<string>>(() => new Set())
const ccbSetByParent = DefaultMap<string, Set<string>>(() => new Set());
for (const k in pvcd) {
const { chem_comp, chem_comp_bond } = pvcd[k]
const { chem_comp, chem_comp_bond } = pvcd[k];
if (chem_comp_bond._rowCount) {
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
if (parentIds.length === 0) {
const set = ccbSetByParent.getDefault(chem_comp.id.value(0))
addChemCompBondToSet(set, chem_comp_bond)
const set = ccbSetByParent.getDefault(chem_comp.id.value(0));
addChemCompBondToSet(set, chem_comp_bond);
} else {
for (let i = 0, il = parentIds.length; i < il; ++i) {
const parentId = parentIds[i]
const set = ccbSetByParent.getDefault(parentId)
addChemCompBondToSet(set, chem_comp_bond)
const parentId = parentIds[i];
const set = ccbSetByParent.getDefault(parentId);
addChemCompBondToSet(set, chem_comp_bond);
}
}
}
}
for (const k in pvcd) {
const { chem_comp, chem_comp_atom, chem_comp_bond } = pvcd[k]
const { chem_comp, chem_comp_atom, chem_comp_bond } = pvcd[k];
if (chem_comp_bond._rowCount) {
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
if (parentIds.length > 0) {
for (let i = 0, il = parentIds.length; i < il; ++i) {
const entryBonds = addChemCompBondToSet(new Set<string>(), chem_comp_bond)
const entryAtoms = addChemCompAtomToSet(new Set<string>(), chem_comp_atom)
const extraBonds = SetUtils.difference(ccbSetByParent.get(parentIds[i])!, entryBonds)
const entryBonds = addChemCompBondToSet(new Set<string>(), chem_comp_bond);
const entryAtoms = addChemCompAtomToSet(new Set<string>(), chem_comp_atom);
const extraBonds = SetUtils.difference(ccbSetByParent.get(parentIds[i])!, entryBonds);
extraBonds.forEach(bk => {
const [a1, a2] = bk.split('|')
const [a1, a2] = bk.split('|');
if (entryAtoms.has(a1) && entryAtoms.has(a2)) {
console.error(`Adding all PVCD bonds would wrongly add bond ${bk} for ${k}`)
console.error(`Adding all PVCD bonds would wrongly add bond ${bk} for ${k}`);
}
})
});
}
}
}
@@ -135,51 +135,51 @@ function checkAddingBondsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
}
async function createBonds() {
await ensureDataAvailable()
const ccd = await readCCD()
const pvcd = await readPVCD()
await ensureDataAvailable();
const ccd = await readCCD();
const pvcd = await readPVCD();
const ccbSet = new Set<string>()
const ccbSet = new Set<string>();
const comp_id: string[] = []
const atom_id_1: string[] = []
const atom_id_2: string[] = []
const value_order: typeof mmCIF_chemCompBond_schema['value_order']['T'][] = []
const pdbx_aromatic_flag: typeof mmCIF_chemCompBond_schema['pdbx_aromatic_flag']['T'][] = []
const pdbx_stereo_config: typeof mmCIF_chemCompBond_schema['pdbx_stereo_config']['T'][] = []
const molstar_protonation_variant: string[] = []
const comp_id: string[] = [];
const atom_id_1: string[] = [];
const atom_id_2: string[] = [];
const value_order: typeof mmCIF_chemCompBond_schema['value_order']['T'][] = [];
const pdbx_aromatic_flag: typeof mmCIF_chemCompBond_schema['pdbx_aromatic_flag']['T'][] = [];
const pdbx_stereo_config: typeof mmCIF_chemCompBond_schema['pdbx_stereo_config']['T'][] = [];
const molstar_protonation_variant: string[] = [];
function addBonds(compId: string, ccb: CCB, protonationVariant: boolean) {
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
const atomId1 = ccb.atom_id_1.value(i)
const atomId2 = ccb.atom_id_2.value(i)
const k = ccbKey(compId, atomId1, atomId2)
const atomId1 = ccb.atom_id_1.value(i);
const atomId2 = ccb.atom_id_2.value(i);
const k = ccbKey(compId, atomId1, atomId2);
if (!ccbSet.has(k)) {
atom_id_1.push(atomId1)
atom_id_2.push(atomId2)
comp_id.push(compId)
value_order.push(ccb.value_order.value(i))
pdbx_aromatic_flag.push(ccb.pdbx_aromatic_flag.value(i))
pdbx_stereo_config.push(ccb.pdbx_stereo_config.value(i))
molstar_protonation_variant.push(protonationVariant ? 'Y' : 'N')
ccbSet.add(k)
atom_id_1.push(atomId1);
atom_id_2.push(atomId2);
comp_id.push(compId);
value_order.push(ccb.value_order.value(i));
pdbx_aromatic_flag.push(ccb.pdbx_aromatic_flag.value(i));
pdbx_stereo_config.push(ccb.pdbx_stereo_config.value(i));
molstar_protonation_variant.push(protonationVariant ? 'Y' : 'N');
ccbSet.add(k);
}
}
}
// check adding bonds from PVCD
checkAddingBondsFromPVCD(pvcd)
checkAddingBondsFromPVCD(pvcd);
// add bonds from PVCD
for (const k in pvcd) {
const { chem_comp, chem_comp_bond } = pvcd[k]
const { chem_comp, chem_comp_bond } = pvcd[k];
if (chem_comp_bond._rowCount) {
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
if (parentIds.length === 0) {
addBonds(chem_comp.id.value(0), chem_comp_bond, false)
addBonds(chem_comp.id.value(0), chem_comp_bond, false);
} else {
for (let i = 0, il = parentIds.length; i < il; ++i) {
addBonds(parentIds[i], chem_comp_bond, true)
addBonds(parentIds[i], chem_comp_bond, true);
}
}
}
@@ -187,43 +187,43 @@ async function createBonds() {
// add bonds from CCD
for (const k in ccd) {
const { chem_comp, chem_comp_bond } = ccd[k]
const { chem_comp, chem_comp_bond } = ccd[k];
if (chem_comp_bond._rowCount) {
addBonds(chem_comp.id.value(0), chem_comp_bond, false)
addBonds(chem_comp.id.value(0), chem_comp_bond, false);
}
}
const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, {
comp_id, atom_id_1, atom_id_2, value_order,
pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
})
});
const bondDatabase = Database.ofTables(
TABLE_NAME,
{ chem_comp_bond: mmCIF_chemCompBond_schema },
{ chem_comp_bond: bondTable }
)
);
return bondDatabase
return bondDatabase;
}
async function run(out: string, binary = false) {
const bonds = await createBonds()
const bonds = await createBonds();
const cif = getEncodedCif(TABLE_NAME, bonds, binary)
const cif = getEncodedCif(TABLE_NAME, bonds, binary);
if (!fs.existsSync(path.dirname(out))) {
fs.mkdirSync(path.dirname(out));
}
writeFile(out, cif)
writeFile(out, cif);
}
const TABLE_NAME = 'CHEM_COMP_BONDS'
const TABLE_NAME = 'CHEM_COMP_BONDS';
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data')
const CCD_PATH = path.join(DATA_DIR, 'components.cif')
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif')
const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'
const PVCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/aa-variants-v1.cif'
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data');
const CCD_PATH = path.join(DATA_DIR, 'components.cif');
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif');
const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif';
const PVCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/aa-variants-v1.cif';
const parser = new argparse.ArgumentParser({
addHelp: true,
@@ -247,6 +247,6 @@ interface Args {
}
const args: Args = parser.parseArgs();
const FORCE_DOWNLOAD = args.forceDownload
const FORCE_DOWNLOAD = args.forceDownload;
run(args.out, args.binary)
run(args.out, args.binary);

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import { CIF, CifCategory, getCifFieldType, CifField, CifFile } from '../../mol-io/reader/cif';
import { CifWriter, EncodingStrategyHint } from '../../mol-io/writer/cif';
import * as util from 'util';
import * as fs from 'fs';
import * as zlib from 'zlib';
import { Progress, Task, RuntimeContext } from '../../mol-task';
import { classifyFloatArray, classifyIntArray } from '../../mol-io/common/binary-cif';
import { BinaryEncodingProvider } from '../../mol-io/writer/cif/encoder/binary';
import { Category } from '../../mol-io/writer/cif/encoder';
import { ReaderResult } from '../../mol-io/reader/result';
function showProgress(p: Progress) {
process.stdout.write(`\r${new Array(80).join(' ')}`);
process.stdout.write(`\r${Progress.format(p)}`);
}
const readFileAsync = util.promisify(fs.readFile);
const unzipAsync = util.promisify<zlib.InputType, Buffer>(zlib.unzip);
async function readFile(ctx: RuntimeContext, filename: string): Promise<ReaderResult<CifFile>> {
const isGz = /\.gz$/i.test(filename);
if (filename.match(/\.bcif/)) {
let input = await readFileAsync(filename);
if (isGz) input = await unzipAsync(input);
return await CIF.parseBinary(new Uint8Array(input)).runInContext(ctx);
} else {
let str: string;
if (isGz) {
const data = await unzipAsync(await readFileAsync(filename));
str = data.toString('utf8');
} else {
str = await readFileAsync(filename, 'utf8');
}
return await CIF.parseText(str).runInContext(ctx);
}
}
async function getCIF(ctx: RuntimeContext, filename: string) {
const parsed = await readFile(ctx, filename);
if (parsed.isError) {
throw new Error(parsed.toString());
}
return parsed.result;
}
function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
return {
name: cat.name,
instance: () => CifWriter.categoryInstance(fields, { data: cat, rowCount: cat.rowCount })
};
}
function classify(name: string, field: CifField): CifWriter.Field {
const type = getCifFieldType(field);
if (type['@type'] === 'str') {
return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
} else if (type['@type'] === 'float') {
const encoder = classifyFloatArray(field.toFloatArray({ array: Float64Array }));
return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, encoder, typedArray: Float64Array });
} else {
const encoder = classifyIntArray(field.toIntArray({ array: Int32Array }));
return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, encoder, typedArray: Int32Array });
}
}
export default function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
return Task.create<Uint8Array>('BinaryCIF', async ctx => {
const encodingProvider: BinaryEncodingProvider = hints
? CifWriter.createEncodingProviderFromJsonConfig(hints)
: { get: (c, f) => void 0 };
const cif = await getCIF(ctx, path);
const encoder = CifWriter.createEncoder({
binary: !asText,
encoderName: 'mol*/ciftools cif2bcif',
binaryAutoClassifyEncoding: true,
binaryEncodingPovider: encodingProvider
});
if (filter) {
encoder.setFilter(Category.filterOf(filter));
}
let maxProgress = 0;
for (const b of cif.blocks) {
maxProgress += b.categoryNames.length;
for (const c of b.categoryNames) maxProgress += b.categories[c].fieldNames.length;
}
let current = 0;
for (const b of cif.blocks) {
encoder.startDataBlock(b.header);
for (const c of b.categoryNames) {
const cat = b.categories[c];
const fields: CifWriter.Field[] = [];
for (const f of cat.fieldNames) {
fields.push(classify(f, cat.getField(f)!));
current++;
if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
}
encoder.writeCategory(getCategoryInstanceProvider(b.categories[c], fields));
current++;
if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
}
}
await ctx.update('Exporting...');
const ret = encoder.getData() as Uint8Array;
await ctx.update('Done.\n');
return ret;
}).run(showProgress, 250);
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse';
import * as util from 'util';
import * as fs from 'fs';
import * as zlib from 'zlib';
import convert from './converter';
require('util.promisify').shim();
async function process(srcPath: string, outPath: string, configPath?: string, filterPath?: string) {
const config = configPath ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : void 0;
const filter = filterPath ? fs.readFileSync(filterPath, 'utf8') : void 0;
const res = await convert(srcPath, false, config, filter);
await write(outPath, res);
}
const zipAsync = util.promisify<zlib.InputType, Buffer>(zlib.gzip);
async function write(outPath: string, res: Uint8Array) {
const isGz = /\.gz$/i.test(outPath);
if (isGz) {
res = await zipAsync(res);
}
fs.writeFileSync(outPath, res);
}
function run(args: Args) {
process(args.src, args.out, args.config, args.filter);
}
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Convert any CIF file to a BCIF file'
});
parser.addArgument([ 'src' ], {
help: 'Source CIF path'
});
parser.addArgument([ 'out' ], {
help: 'Output BCIF path'
});
parser.addArgument([ '-c', '--config' ], {
help: 'Optional encoding strategy/precision config path',
required: false
});
parser.addArgument([ '-f', '--filter' ], {
help: 'Optional filter whitelist/blacklist path',
required: false
});
interface Args {
src: string
out: string
config?: string
filter?: string
}
const args: Args = parser.parseArgs();
if (args) {
run(args);
}

274
src/apps/cifschema/index.ts Normal file
View File

@@ -0,0 +1,274 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse';
import * as fs from 'fs';
import * as path from 'path';
import fetch from 'node-fetch';
import { parseCsv } from '../../mol-io/reader/csv/parser';
import { CifFrame, CifBlock } from '../../mol-io/reader/cif';
import parseText from '../../mol-io/reader/cif/text/parser';
import { generateSchema } from './util/cif-dic';
import { generate } from './util/generate';
import { Filter, Database } from './util/schema';
import { parseImportGet } from './util/helper';
function getDicVersion(block: CifBlock) {
return block.categories.dictionary.getField('version')!.str(0);
}
function getDicNamespace(block: CifBlock) {
return block.categories.dictionary.getField('namespace')!.str(0);
}
async function runGenerateSchemaMmcif(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
await ensureMmcifDicAvailable();
const mmcifDic = await parseText(fs.readFileSync(MMCIF_DIC_PATH, 'utf8')).run();
if (mmcifDic.isError) throw mmcifDic;
await ensureIhmDicAvailable();
const ihmDic = await parseText(fs.readFileSync(IHM_DIC_PATH, 'utf8')).run();
if (ihmDic.isError) throw ihmDic;
await ensureCarbBranchDicAvailable();
const carbBranchDic = await parseText(fs.readFileSync(CARB_BRANCH_DIC_PATH, 'utf8')).run();
if (carbBranchDic.isError) throw carbBranchDic;
await ensureCarbCompDicAvailable();
const carbCompDic = await parseText(fs.readFileSync(CARB_COMP_DIC_PATH, 'utf8')).run();
if (carbCompDic.isError) throw carbCompDic;
const mmcifDicVersion = getDicVersion(mmcifDic.result.blocks[0]);
const ihmDicVersion = getDicVersion(ihmDic.result.blocks[0]);
const carbDicVersion = 'draft';
const version = `Dictionary versions: mmCIF ${mmcifDicVersion}, IHM ${ihmDicVersion}, CARB ${carbDicVersion}.`;
const frames: CifFrame[] = [...mmcifDic.result.blocks[0].saveFrames, ...ihmDic.result.blocks[0].saveFrames, ...carbBranchDic.result.blocks[0].saveFrames, ...carbCompDic.result.blocks[0].saveFrames];
const schema = generateSchema(frames);
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
}
async function runGenerateSchemaCifCore(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
await ensureCifCoreDicAvailable();
const cifCoreDic = await parseText(fs.readFileSync(CIF_CORE_DIC_PATH, 'utf8')).run();
if (cifCoreDic.isError) throw cifCoreDic;
const cifCoreDicVersion = getDicVersion(cifCoreDic.result.blocks[0]);
const version = `Dictionary versions: CifCore ${cifCoreDicVersion}.`;
const frames: CifFrame[] = [...cifCoreDic.result.blocks[0].saveFrames];
const imports = await resolveImports(frames, DIC_DIR);
const schema = generateSchema(frames, imports);
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
}
async function resolveImports(frames: CifFrame[], baseDir: string): Promise<Map<string, CifFrame[]>> {
const imports = new Map<string, CifFrame[]>();
for (const d of frames) {
if ('import' in d.categories) {
const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0));
for (const g of importGet) {
const { file } = g;
if (!file) continue;
if (imports.has(file)) continue;
const dic = await parseText(fs.readFileSync(path.join(baseDir, file), 'utf8')).run();
if (dic.isError) throw dic;
imports.set(file, [...dic.result.blocks[0].saveFrames]);
}
}
}
return imports;
}
async function runGenerateSchemaDic(name: string, dicPath: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
const dic = await parseText(fs.readFileSync(dicPath, 'utf8')).run();
if (dic.isError) throw dic;
const dicVersion = getDicVersion(dic.result.blocks[0]);
const dicName = getDicNamespace(dic.result.blocks[0]);
const version = `Dictionary versions: ${dicName} ${dicVersion}.`;
const frames: CifFrame[] = [...dic.result.blocks[0].saveFrames];
const imports = await resolveImports(frames, path.dirname(dicPath));
const schema = generateSchema(frames, imports);
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
}
async function runGenerateSchema(name: string, version: string, schema: Database, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
const filter = fieldNamesPath ? await getFieldNamesFilter(fieldNamesPath) : undefined;
const output = typescript ? generate(name, version, schema, filter, moldbImportPath, addAliases) : JSON.stringify(schema, undefined, 4);
if (out) {
fs.writeFileSync(out, output);
} else {
console.log(output);
}
}
async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
const fieldNamesStr = fs.readFileSync(fieldNamesPath, 'utf8');
const parsed = await parseCsv(fieldNamesStr, { noColumnNames: true }).run();
if (parsed.isError) throw parser.error;
const csvFile = parsed.result;
const fieldNamesCol = csvFile.table.getColumn('0');
if (!fieldNamesCol) throw 'error getting fields columns';
const fieldNames = fieldNamesCol.toStringArray();
const filter: Filter = {};
fieldNames.forEach((name, i) => {
const [ category, field ] = name.split('.');
// console.log(category, field)
if (!filter[ category ]) filter[ category ] = {};
filter[ category ][ field ] = true;
});
return filter;
}
async function ensureMmcifDicAvailable() { await ensureDicAvailable(MMCIF_DIC_PATH, MMCIF_DIC_URL); }
async function ensureIhmDicAvailable() { await ensureDicAvailable(IHM_DIC_PATH, IHM_DIC_URL); }
async function ensureCarbBranchDicAvailable() { await ensureDicAvailable(CARB_BRANCH_DIC_PATH, CARB_BRANCH_DIC_URL); }
async function ensureCarbCompDicAvailable() { await ensureDicAvailable(CARB_COMP_DIC_PATH, CARB_COMP_DIC_URL); }
async function ensureCifCoreDicAvailable() {
await ensureDicAvailable(CIF_CORE_DIC_PATH, CIF_CORE_DIC_URL);
await ensureDicAvailable(CIF_CORE_ENUM_PATH, CIF_CORE_ENUM_URL);
await ensureDicAvailable(CIF_CORE_ATTR_PATH, CIF_CORE_ATTR_URL);
}
async function ensureDicAvailable(dicPath: string, dicUrl: string) {
if (FORCE_DIC_DOWNLOAD || !fs.existsSync(dicPath)) {
const name = dicUrl.substr(dicUrl.lastIndexOf('/') + 1);
console.log(`downloading ${name}...`);
const data = await fetch(dicUrl);
if (!fs.existsSync(DIC_DIR)) {
fs.mkdirSync(DIC_DIR);
}
fs.writeFileSync(dicPath, await data.text());
console.log(`done downloading ${name}`);
}
}
const DIC_DIR = path.resolve(__dirname, '../../../build/dics/');
const MMCIF_DIC_PATH = `${DIC_DIR}/mmcif_pdbx_v50.dic`;
const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic';
const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`;
const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic';
const CARB_BRANCH_DIC_PATH = `${DIC_DIR}/entity_branch-extension.dic`;
const CARB_BRANCH_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/entity_branch-extension.dic';
const CARB_COMP_DIC_PATH = `${DIC_DIR}/chem_comp-extension.dic`;
const CARB_COMP_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/chem_comp-extension.dic';
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';
const CIF_CORE_ENUM_PATH = `${DIC_DIR}/templ_enum.cif`;
const CIF_CORE_ENUM_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_enum.cif';
const CIF_CORE_ATTR_PATH = `${DIC_DIR}/templ_attr.cif`;
const CIF_CORE_ATTR_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_attr.cif';
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Create schema from mmcif dictionary (v50 plus IHM and entity_branch extensions, downloaded from wwPDB)'
});
parser.addArgument([ '--preset', '-p' ], {
defaultValue: '',
choices: ['', 'mmCIF', 'CCD', 'BIRD', 'CifCore'],
help: 'Preset name'
});
parser.addArgument([ '--name', '-n' ], {
defaultValue: '',
help: 'Schema name'
});
parser.addArgument([ '--out', '-o' ], {
help: 'Generated schema output path, if not given printed to stdout'
});
parser.addArgument([ '--targetFormat', '-tf' ], {
defaultValue: 'typescript-molstar',
choices: ['typescript-molstar', 'json-internal'],
help: 'Target format'
});
parser.addArgument([ '--dicPath', '-d' ], {
defaultValue: '',
help: 'Path to dictionary'
});
parser.addArgument([ '--fieldNamesPath', '-fn' ], {
defaultValue: '',
help: 'Field names to include'
});
parser.addArgument([ '--forceDicDownload', '-f' ], {
action: 'storeTrue',
help: 'Force download of dictionaries'
});
parser.addArgument([ '--moldataImportPath', '-mip' ], {
defaultValue: 'molstar/lib/mol-data',
help: 'mol-data import path (for typescript target only)'
});
parser.addArgument([ '--addAliases', '-aa' ], {
action: 'storeTrue',
help: 'Add field name/path aliases'
});
interface Args {
name: string
preset: '' | 'mmCIF' | 'CCD' | 'BIRD' | 'CifCore'
forceDicDownload: boolean
dic: '' | 'mmCIF' | 'CifCore'
dicPath: string,
fieldNamesPath: string
targetFormat: 'typescript-molstar' | 'json-internal'
out: string,
moldataImportPath: string
addAliases: boolean
}
const args: Args = parser.parseArgs();
const FORCE_DIC_DOWNLOAD = args.forceDicDownload;
switch (args.preset) {
case 'mmCIF':
args.name = 'mmCIF';
args.dic = 'mmCIF';
args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/mmcif-field-names.csv');
break;
case 'CCD':
args.name = 'CCD';
args.dic = 'mmCIF';
args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/ccd-field-names.csv');
break;
case 'BIRD':
args.name = 'BIRD';
args.dic = 'mmCIF';
args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/bird-field-names.csv');
break;
case 'CifCore':
args.name = 'CifCore';
args.dic = 'CifCore';
args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/cif-core-field-names.csv');
break;
}
if (args.name) {
const typescript = args.targetFormat === 'typescript-molstar';
if (args.dicPath) {
runGenerateSchemaDic(args.name, args.dicPath, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
console.error(e);
});
} else if (args.dic === 'mmCIF') {
runGenerateSchemaMmcif(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
console.error(e);
});
} else if (args.dic === 'CifCore') {
runGenerateSchemaCifCore(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
console.error(e);
});
}
}

View File

@@ -0,0 +1,475 @@
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Database, Column, EnumCol, StrCol, IntCol, ListCol, FloatCol, CoordCol, MatrixCol, VectorCol } from './schema';
import { parseImportGet } from './helper';
import * as Data from '../../../mol-io/reader/cif/data-model';
import { CifFrame } from '../../../mol-io/reader/cif/data-model';
export function getFieldType(type: string, description: string, values?: string[], container?: string): Column {
switch (type) {
// mmCIF
case 'code':
case 'ucode':
case 'line':
case 'uline':
case 'text':
case 'char':
case 'uchar3':
case 'uchar1':
case 'boolean':
return values && values.length ? EnumCol(values, 'str', description) : StrCol(description);
case 'aliasname':
case 'name':
case 'idname':
case 'any':
case 'atcode':
case 'fax':
case 'phone':
case 'email':
case 'code30':
case 'seq-one-letter-code':
case 'author':
case 'orcid_id':
case 'sequence_dep':
case 'pdb_id':
case 'emd_id':
// todo, consider adding specialised fields
case 'yyyy-mm-dd':
case 'yyyy-mm-dd:hh:mm':
case 'yyyy-mm-dd:hh:mm-flex':
case 'int-range':
case 'float-range':
case 'binary':
case 'operation_expression':
case 'point_symmetry':
case '4x3_matrix':
case '3x4_matrices':
case 'point_group':
case 'point_group_helical':
case 'symmetry_operation':
case 'date_dep':
case 'url':
case 'symop':
case 'exp_data_doi':
case 'asym_id':
return StrCol(description);
case 'int':
case 'non_negative_int':
case 'positive_int':
return values && values.length ? EnumCol(values, 'int', description) : IntCol(description);
case 'float':
return FloatCol(description);
case 'ec-type':
case 'ucode-alphanum-csv':
case 'id_list':
return ListCol('str', ',', description);
case 'id_list_spc':
return ListCol('str', ' ', description);
// cif
case 'Text':
case 'Code':
case 'Complex':
case 'Symop':
case 'List':
case 'List(Real,Real)':
case 'List(Real,Real,Real,Real)':
case 'Date':
case 'Datetime':
case 'Tag':
case 'Implied':
return wrapContainer('str', ',', description, container);
case 'Real':
return wrapContainer('float', ',', description, container);
case 'Integer':
return wrapContainer('int', ',', description, container);
}
console.log(`unknown type '${type}'`);
return StrCol(description);
}
function ColFromType(type: 'int' | 'str' | 'float' | 'coord', description: string): Column {
switch (type) {
case 'int': return IntCol(description);
case 'str': return StrCol(description);
case 'float': return FloatCol(description);
case 'coord': return CoordCol(description);
}
}
function wrapContainer(type: 'int' | 'str' | 'float' | 'coord', separator: string, description: string, container?: string) {
return container && container === 'List' ? ListCol(type, separator, description) : ColFromType(type, description);
}
type FrameCategories = { [category: string]: Data.CifFrame }
type FrameLinks = { [k: string]: string }
interface FrameData {
categories: FrameCategories
links: FrameLinks
}
type Imports = Map<string, CifFrame[]>
function getImportFrames(d: Data.CifFrame, imports: Imports) {
const frames: Data.CifFrame[] = [];
if (!('import' in d.categories)) return frames;
const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0));
for (const g of importGet) {
const { file, save } = g;
if (!file || !save) {
console.warn(`missing 'save' or 'file' for import in '${d.header}'`);
continue;
}
const importFrames = imports.get(file);
if (!importFrames) {
console.warn(`missing '${file}' entry in imports`);
continue;
}
const importSave = importFrames.find(id => id.header.toLowerCase() === save.toLowerCase());
if (!importSave) {
console.warn(`missing '${save}' save frame in '${file}'`);
continue;
}
frames.push(importSave);
}
return frames;
}
/** get field from given or linked category */
function getField(category: string, field: string, d: Data.CifFrame, imports: Imports, ctx: FrameData): Data.CifField|undefined {
const { categories, links } = ctx;
const cat = d.categories[category];
if (cat) {
return cat.getField(field);
} else if (d.header in links) {
const linkName = links[d.header];
if (linkName in categories) {
return getField(category, field, categories[linkName], imports, ctx);
} else {
// console.log(`link '${linkName}' not found`)
}
} else {
const importFrames = getImportFrames(d, imports);
for (const idf of importFrames) {
return getField(category, field, idf, imports, ctx);
}
}
}
function getEnums(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
const value = getField('item_enumeration', 'value', d, imports, ctx);
const enums: string[] = [];
if (value) {
for (let i = 0; i < value.rowCount; ++i) {
enums.push(value.str(i));
// console.log(value.str(i))
}
return enums;
} else {
// console.log(`item_enumeration.value not found for '${d.header}'`)
}
}
function getContainer(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
const value = getField('type', 'container', d, imports, ctx);
return value ? value.str(0) : undefined;
}
function getCode(d: Data.CifFrame, imports: Imports, ctx: FrameData): [string, string[] | undefined, string | undefined ] | undefined {
const code = getField('item_type', 'code', d, imports, ctx) || getField('type', 'contents', d, imports, ctx);
if (code) {
return [ code.str(0), getEnums(d, imports, ctx), getContainer(d, imports, ctx) ];
} else {
console.log(`item_type.code or type.contents not found for '${d.header}'`);
}
}
function getSubCategory(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
const value = getField('item_sub_category', 'id', d, imports, ctx);
if (value) {
return value.str(0);
}
}
function getDescription(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
const value = getField('item_description', 'description', d, imports, ctx) || getField('description', 'text', d, imports, ctx);
if (value) {
// trim (after newlines) and remove references to square brackets
return value.str(0).trim()
.replace(/(\r\n|\r|\n)([ \t]+)/g, '\n')
.replace(/(\[[1-3]\])+ element/, 'elements')
.replace(/(\[[1-3]\])+/, '');
}
}
function getAliases(d: Data.CifFrame, imports: Imports, ctx: FrameData): string[] | undefined {
const value = getField('item_aliases', 'alias_name', d, imports, ctx) || getField('alias', 'definition_id', d, imports, ctx);
return value ? value.toStringArray().map(v => v.substr(1)) : undefined;
}
const reMatrixField = /\[[1-3]\]\[[1-3]\]/;
const reVectorField = /\[[1-3]\]/;
const FORCE_INT_FIELDS = [
'_atom_site.id',
'_atom_site.auth_seq_id',
'_atom_site_anisotrop.id',
'_pdbx_struct_mod_residue.auth_seq_id',
'_struct_conf.beg_auth_seq_id',
'_struct_conf.end_auth_seq_id',
'_struct_conn.ptnr1_auth_seq_id',
'_struct_conn.ptnr2_auth_seq_id',
'_struct_sheet_range.beg_auth_seq_id',
'_struct_sheet_range.end_auth_seq_id',
];
const FORCE_MATRIX_FIELDS_MAP: { [k: string]: string } = {
'atom_site_aniso.U_11': 'U',
'atom_site_aniso.U_22': 'U',
'atom_site_aniso.U_33': 'U',
'atom_site_aniso.U_23': 'U',
'atom_site_aniso.U_13': 'U',
'atom_site_aniso.U_12': 'U',
'atom_site_aniso.U_11_su': 'U_su',
'atom_site_aniso.U_22_su': 'U_su',
'atom_site_aniso.U_33_su': 'U_su',
'atom_site_aniso.U_23_su': 'U_su',
'atom_site_aniso.U_13_su': 'U_su',
'atom_site_aniso.U_12_su': 'U_su',
};
const FORCE_MATRIX_FIELDS = Object.keys(FORCE_MATRIX_FIELDS_MAP);
const EXTRA_ALIASES: Database['aliases'] = {
'atom_site_aniso.U': [
'atom_site_anisotrop_U'
],
'atom_site_aniso.U_su': [
'atom_site_aniso_U_esd',
'atom_site_anisotrop_U_esd',
],
};
const COMMA_SEPARATED_LIST_FIELDS = [
'_atom_site.pdbx_struct_group_id',
'_chem_comp.mon_nstd_parent_comp_id',
'_diffrn_radiation.pdbx_wavelength_list',
'_diffrn_source.pdbx_wavelength_list',
'_em_diffraction.tilt_angle_list', // 20,40,50,55
'_em_entity_assembly.entity_id_list',
'_entity.pdbx_description', // Endolysin,Beta-2 adrenergic receptor
'_entity.pdbx_ec',
'_entity_poly.pdbx_strand_id', // A,B
'_entity_src_gen.pdbx_gene_src_gene', // ADRB2, ADRB2R, B2AR
'_pdbx_depui_entry_details.experimental_methods',
'_pdbx_depui_entry_details.requested_accession_types',
'_pdbx_soln_scatter_model.software_list', // INSIGHT II, HOMOLOGY, DISCOVERY, BIOPOLYMER, DELPHI
'_pdbx_soln_scatter_model.software_author_list', // MSI
'_pdbx_soln_scatter_model.entry_fitting_list', // Odd example: 'PDB CODE 1HFI, 1HCC, 1HFH, 1VCC'
'_pdbx_struct_assembly_gen.entity_inst_id',
'_pdbx_struct_assembly_gen.asym_id_list',
'_pdbx_struct_assembly_gen.auth_asym_id_list',
'_pdbx_struct_assembly_gen_depositor_info.asym_id_list',
'_pdbx_struct_assembly_gen_depositor_info.chain_id_list',
'_pdbx_struct_group_list.group_enumeration_type',
'_reflns.pdbx_diffrn_id',
'_refine.pdbx_diffrn_id',
'_reflns_shell.pdbx_diffrn_id',
'_struct_keywords.text',
];
const SPACE_SEPARATED_LIST_FIELDS = [
'_chem_comp.pdbx_subcomponent_list', // TSM DPH HIS CHF EMR
'_pdbx_soln_scatter.data_reduction_software_list', // OTOKO
'_pdbx_soln_scatter.data_analysis_software_list', // SCTPL5 GNOM
];
const SEMICOLON_SEPARATED_LIST_FIELDS = [
'_chem_comp.pdbx_synonyms' // GLYCERIN; PROPANE-1,2,3-TRIOL
];
/**
* Useful when a dictionary extension will add enum values to an existing dictionary.
* By adding them here, the dictionary extension can be tested before the added enum
* values are available in the existing dictionary.
*/
const EXTRA_ENUM_VALUES: { [k: string]: string[] } = {
};
export function generateSchema(frames: CifFrame[], imports: Imports = new Map()): Database {
const tables: Database['tables'] = {};
const aliases: Database['aliases'] = { ...EXTRA_ALIASES };
const categories: FrameCategories = {};
const links: FrameLinks = {};
const ctx = { categories, links };
// get category metadata
frames.forEach(d => {
// category definitions in mmCIF start with '_' and don't include a '.'
// category definitions in cifCore don't include a '.'
if (d.header[0] === '_' || d.header.includes('.')) return;
const categoryName = d.header.toLowerCase();
// console.log(d.header, d.categoryNames, d.categories)
let descriptionField: Data.CifField | undefined;
const categoryKeyNames = new Set<string>();
if ('category' in d.categories && 'category_key' in d.categories) {
const category = d.categories['category'];
const categoryKey = d.categories['category_key'];
if (categoryKey) {
const categoryKey_names = categoryKey.getField('name');
if (categoryKey_names) {
for (let i = 0, il = categoryKey_names.rowCount; i < il; ++i) {
categoryKeyNames.add(categoryKey_names.str(i));
}
}
}
descriptionField = category.getField('description');
if (categoryKeyNames.size === 0) {
console.log(`no key given for category '${categoryName}'`);
}
}
if ('description' in d.categories) {
descriptionField = d.categories['description'].getField('text');
}
let description = '';
if (descriptionField) {
description = descriptionField.str(0).trim()
.replace(/(\r\n|\r|\n)([ \t]+)/g, '\n'); // remove padding after newlines
} else {
console.log(`no description given for category '${categoryName}'`);
}
tables[categoryName] = { description, key: categoryKeyNames, columns: {} };
// console.log('++++++++++++++++++++++++++++++++++++++++++')
// console.log('name', categoryName)
// console.log('desc', description)
// console.log('key', categoryKeyNames)
});
// build list of links between categories
frames.forEach(d => {
if (d.header[0] !== '_' && !d.header.includes('.')) return;
categories[d.header] = d;
const item_linked = d.categories['item_linked'];
if (item_linked) {
const child_name = item_linked.getField('child_name');
const parent_name = item_linked.getField('parent_name');
if (child_name && parent_name) {
for (let i = 0; i < item_linked.rowCount; ++i) {
const childName = child_name.str(i);
const parentName = parent_name.str(i);
if (childName in links && links[childName] !== parentName) {
console.log(`${childName} linked to ${links[childName]}, ignoring link to ${parentName}`);
}
links[childName] = parentName;
}
}
}
});
// get field data
Object.keys(categories).forEach(fullName => {
const d = categories[fullName];
if (!d) {
console.log(`'${fullName}' not found, moving on`);
return;
}
const categoryName = d.header.substring(d.header[0] === '_' ? 1 : 0, d.header.indexOf('.'));
const itemName = d.header.substring(d.header.indexOf('.') + 1);
let fields: { [k: string]: Column };
if (categoryName in tables) {
fields = tables[categoryName].columns;
tables[categoryName].key.add(itemName);
} else if (categoryName.toLowerCase() in tables) {
// take case from category name in 'field' data as it is better if data is from cif dictionaries
tables[categoryName] = tables[categoryName.toLowerCase()];
fields = tables[categoryName].columns;
} else {
console.log(`category '${categoryName}' has no metadata`);
fields = {};
tables[categoryName] = {
description: '',
key: new Set(),
columns: fields
};
}
const itemAliases = getAliases(d, imports, ctx);
if (itemAliases) aliases[`${categoryName}.${itemName}`] = itemAliases;
const description = getDescription(d, imports, ctx) || '';
// need to use regex to check for matrix or vector items
// as sub_category assignment is missing for some entries
const subCategory = getSubCategory(d, imports, ctx);
if (subCategory === 'cartesian_coordinate' || subCategory === 'fractional_coordinate') {
fields[itemName] = CoordCol(description);
} else if (FORCE_INT_FIELDS.includes(d.header)) {
fields[itemName] = IntCol(description);
console.log(`forcing int: ${d.header}`);
} else if (FORCE_MATRIX_FIELDS.includes(d.header)) {
fields[itemName] = FloatCol(description);
fields[FORCE_MATRIX_FIELDS_MAP[d.header]] = MatrixCol(3, 3, description);
console.log(`forcing matrix: ${d.header}`);
} else if (subCategory === 'matrix') {
fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description);
} else if (subCategory === 'vector') {
fields[itemName.replace(reVectorField, '')] = VectorCol(3, description);
} else {
if (itemName.match(reMatrixField)) {
fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description);
console.log(`${d.header} should have 'matrix' _item_sub_category.id`);
} else if (itemName.match(reVectorField)) {
fields[itemName.replace(reVectorField, '')] = VectorCol(3, description);
console.log(`${d.header} should have 'vector' _item_sub_category.id`);
} else {
const code = getCode(d, imports, ctx);
if (code) {
let fieldType = getFieldType(code[0], description, code[1], code[2]);
if (fieldType.type === 'str') {
if (COMMA_SEPARATED_LIST_FIELDS.includes(d.header)) {
fieldType = ListCol('str', ',', description);
console.log(`forcing comma separated: ${d.header}`);
} else if (SPACE_SEPARATED_LIST_FIELDS.includes(d.header)) {
fieldType = ListCol('str', ' ', description);
console.log(`forcing space separated: ${d.header}`);
} else if (SEMICOLON_SEPARATED_LIST_FIELDS.includes(d.header)) {
fieldType = ListCol('str', ';', description);
console.log(`forcing space separated: ${d.header}`);
}
}
if (d.header in EXTRA_ENUM_VALUES) {
if (fieldType.type === 'enum') {
fieldType.values.push(...EXTRA_ENUM_VALUES[d.header]);
} else {
console.warn(`expected enum: ${d.header}`);
}
}
fields[itemName] = fieldType;
} else {
fields[itemName] = StrCol(description);
// console.log(`could not determine code for '${d.header}'`)
}
}
}
});
return { tables, aliases };
}

View File

@@ -0,0 +1,151 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Database, Filter, Column } from './schema';
import { indentString } from '../../../mol-util/string';
import { FieldPath } from '../../../mol-io/reader/cif/schema';
function header (name: string, info: string, moldataImportPath: string) {
return `/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated '${name}' schema file. ${info}
*
* @author molstar/ciftools package
*/
import { Database, Column } from '${moldataImportPath}/db'
import Schema = Column.Schema`;
}
function footer (name: string) {
return `
export type ${name}_Schema = typeof ${name}_Schema;
export interface ${name}_Database extends Database<${name}_Schema> {}`;
}
function getTypeShorthands(schema: Database, fields?: Filter) {
const types = new Set<string>();
Object.keys(schema.tables).forEach(table => {
if (fields && !fields[table]) return;
const { columns } = schema.tables[table];
Object.keys(columns).forEach(columnName => {
if (fields && !fields[table][columnName]) return;
types.add(schema.tables[table].columns[columnName].type);
});
});
const shorthands: string[] = [];
types.forEach(type => {
switch (type) {
case 'str': shorthands.push('const str = Schema.str;'); break;
case 'int': shorthands.push('const int = Schema.int;'); break;
case 'float': shorthands.push('const float = Schema.float;'); break;
case 'coord': shorthands.push('const coord = Schema.coord;'); break;
case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break;
case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break;
case 'vector': shorthands.push('const Vector = Schema.Vector;'); break;
case 'list': shorthands.push('const List = Schema.List;'); break;
}
});
return shorthands.join('\n');
}
function getTypeDef(c: Column): string {
switch (c.type) {
case 'str': return 'str';
case 'int': return 'int';
case 'float': return 'float';
case 'coord': return 'coord';
case 'enum':
return `Aliased<'${c.values.map(v => v.replace(/'/g, '\\\'')).join(`' | '`)}'>(${c.subType})`;
case 'matrix':
return `Matrix(${c.rows}, ${c.columns})`;
case 'vector':
return `Vector(${c.length})`;
case 'list':
if (c.subType === 'int') {
return `List('${c.separator}', x => parseInt(x, 10))`;
} else if (c.subType === 'float' || c.subType === 'coord') {
return `List('${c.separator}', x => parseFloat(x))`;
} else {
return `List('${c.separator}', x => x)`;
}
}
}
const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'`; }
function doc(description: string, spacesCount: number) {
const spaces = ' '.repeat(spacesCount);
return [
`${spaces}/**`,
`${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'),
`${spaces} */`
].join('\n');
}
export function generate (name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
const codeLines: string[] = [];
if (fields) {
Object.keys(fields).forEach(table => {
if (table in schema.tables) {
const schemaTable = schema.tables[table];
Object.keys(fields[table]).forEach(column => {
if (!(column in schemaTable.columns)) {
console.log(`filter field '${table}.${column}' not found in schema`);
}
});
} else {
console.log(`filter category '${table}' not found in schema`);
}
});
}
codeLines.push(`export const ${name}_Schema = {`);
Object.keys(schema.tables).forEach(table => {
if (fields && !fields[table]) return;
const { description, columns } = schema.tables[table];
if (description) codeLines.push(doc(description, 4));
codeLines.push(` ${safePropertyString(table)}: {`);
Object.keys(columns).forEach(columnName => {
if (fields && !fields[table][columnName]) return;
const c = columns[columnName];
const typeDef = getTypeDef(c);
if (c.description) codeLines.push(doc(c.description, 8));
codeLines.push(` ${safePropertyString(columnName)}: ${typeDef},`);
});
codeLines.push(' },');
});
codeLines.push('}');
if (addAliases) {
codeLines.push('');
codeLines.push(`export const ${name}_Aliases = {`);
Object.keys(schema.aliases).forEach(path => {
const [ table, columnName ] = path.split('.');
if (fields && !fields[table]) return;
if (fields && !fields[table][columnName]) return;
const filteredAliases = new Set<string>();
schema.aliases[path].forEach(p => {
if (!FieldPath.equal(p, path)) filteredAliases.add(FieldPath.canonical(p));
});
if (filteredAliases.size === 0) return;
codeLines.push(` ${safePropertyString(path)}: [`);
filteredAliases.forEach(alias => {
codeLines.push(` '${alias}',`);
});
codeLines.push(' ],');
});
codeLines.push('}');
}
return `${header(name, info, moldataImportPath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}`;
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export type Import = { save?: string, file?: string }
export function parseImportGet(s: string): Import[] {
// [{'save':hi_ang_Fox_coeffs 'file':templ_attr.cif} {'save':hi_ang_Fox_c0 'file':templ_enum.cif}]
// [{"file":'templ_enum.cif' "save":'H_M_ref'}]
return s.trim().substring(2, s.length - 2).split(/}[ \n\t]*{/g).map(s => {
const save = s.match(/('save'|"save"):([^ \t\n]+)/);
const file = s.match(/('file'|"file"):([^ \t\n]+)/);
return {
save: save ? save[0].substr(7).replace(/['"]/g, '') : undefined,
file: file ? file[0].substr(7).replace(/['"]/g, '') : undefined
};
});
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export interface Database {
tables: { [ tableName: string ]: Table }
aliases: { [ path: string ]: string[] }
}
export interface Table {
description: string
key: Set<string>
columns: { [ columnName: string ]: Column }
}
export type Column = IntCol | StrCol | FloatCol | CoordCol | EnumCol | VectorCol | MatrixCol | ListCol
type BaseCol = { description: string }
export type IntCol = { type: 'int' } & BaseCol
export function IntCol(description: string): IntCol { return { type: 'int', description }; }
export type StrCol = { type: 'str' } & BaseCol
export function StrCol(description: string): StrCol { return { type: 'str', description }; }
export type FloatCol = { type: 'float' } & BaseCol
export function FloatCol(description: string): FloatCol { return { type: 'float', description }; }
export type CoordCol = { type: 'coord' } & BaseCol
export function CoordCol(description: string): CoordCol { return { type: 'coord', description }; }
export type EnumCol = { type: 'enum', subType: 'int' | 'str', values: string[] } & BaseCol
export function EnumCol(values: string[], subType: 'int' | 'str', description: string): EnumCol {
return { type: 'enum', description, values, subType };
}
export type VectorCol = { type: 'vector', length: number } & BaseCol
export function VectorCol(length: number, description: string): VectorCol {
return { type: 'vector', description, length };
}
export type MatrixCol = { type: 'matrix', rows: number, columns: number } & BaseCol
export function MatrixCol(columns: number, rows: number, description: string): MatrixCol {
return { type: 'matrix', description, columns, rows };
}
export type ListCol = { type: 'list', subType: 'int' | 'str' | 'float' | 'coord', separator: string } & BaseCol
export function ListCol(subType: 'int' | 'str' | 'float' | 'coord', separator: string, description: string): ListCol {
return { type: 'list', description, separator, subType };
}
export type Filter = { [ table: string ]: { [ column: string ]: true } }
export function mergeFilters (...filters: Filter[]) {
const n = filters.length;
const mergedFilter: Filter = {};
const fields: Map<string, number> = new Map();
filters.forEach(filter => {
Object.keys(filter).forEach(category => {
Object.keys(filter[ category ]).forEach(field => {
const key = `${category}.${field}`;
const value = fields.get(key) || 0;
fields.set(key, value + 1);
});
});
});
fields.forEach((v, k) => {
if (v !== n) return;
const [categoryName, fieldName] = k.split('.');
if (categoryName in mergedFilter) {
mergedFilter[categoryName][fieldName] = true;
} else {
mergedFilter[categoryName] = { fieldName: true };
}
});
return mergedFilter;
}

View File

@@ -1,178 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
import './index.html'
import { PluginContext } from '../../../mol-plugin/context';
import { PluginCommands } from '../../../mol-plugin/commands';
import { StateTransforms } from '../../../mol-plugin-state/transforms';
import { PluginStateObject as PSO } from '../../../mol-plugin-state/objects';
import { StateBuilder } from '../../../mol-state';
import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
import { createStructureRepresentationParams } from '../../../mol-plugin-state/helpers/structure-representation-params';
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
const Canvas3DPresets = {
illustrative: {
multiSample: {
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusionEnable: true,
occlusionBias: 0.8,
occlusionKernelSize: 6,
outlineEnable: true,
},
renderer: {
ambientIntensity: 1,
lightIntensity: 0,
}
},
occlusion: {
multiSample: {
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusionEnable: true,
occlusionBias: 0.8,
occlusionKernelSize: 6,
outlineEnable: false,
},
renderer: {
ambientIntensity: 0.4,
lightIntensity: 0.6,
}
},
standard: {
multiSample: {
mode: 'off' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusionEnable: false,
outlineEnable: false,
},
renderer: {
ambientIntensity: 0.4,
lightIntensity: 0.6,
}
}
}
type Canvas3DPreset = keyof typeof Canvas3DPresets
function getPreset(preset: Canvas3DPreset) {
switch (preset) {
case 'illustrative': return Canvas3DPresets['illustrative']
case 'standard': return Canvas3DPresets['standard']
case 'occlusion': return Canvas3DPresets['occlusion']
}
}
class LightingDemo {
plugin: PluginContext;
init(target: string | HTMLElement) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec,
layout: {
initial: {
isExpanded: false,
showControls: false
},
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
}
});
this.setPreset('illustrative');
}
setPreset(preset: Canvas3DPreset) {
const props = getPreset(preset)
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
...props,
multiSample: {
...this.plugin.canvas3d!.props.multiSample,
...props.multiSample
},
renderer: {
...this.plugin.canvas3d!.props.renderer,
...props.renderer
},
postprocessing: {
...this.plugin.canvas3d!.props.postprocessing,
...props.postprocessing
},
}});
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
}
private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
const parsed = format === 'cif'
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
return visualRoot;
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
} else if (this.loadedParams.url === url) {
if (state.select('asm').length > 0) loadType = 'update';
}
let tree: StateBuilder.Root;
if (loadType === 'full') {
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
}
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset(this.plugin, { });
}
}
(window as any).LightingDemo = new LightingDemo();

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as _ from '../../mol-plugin-state/transforms'
import * as _ from '../../mol-plugin-state/transforms';
import { StateTransformer, StateObject } from '../../mol-state';
import { StringBuilder } from '../../mol-util';
import * as fs from 'fs';
@@ -13,7 +13,7 @@ import { PluginContext } from '../../mol-plugin/context';
import { ParamDefinition } from '../../mol-util/param-definition';
// force the transform to be evaluated
_.StateTransforms.Data.Download.id
_.StateTransforms.Data.Download.id;
// Empty plugin context
const ctx = new PluginContext({
@@ -32,7 +32,7 @@ function writeTransformer(t: StateTransformer) {
StringBuilder.write(builder, `## <a name="${t.id.replace('.', '-')}"></a>${t.id} :: ${typeToString(t.definition.from)} -> ${typeToString(t.definition.to)}`);
StringBuilder.newline(builder);
if (t.definition.display.description) {
StringBuilder.write(builder, `*${t.definition.display.description}*`)
StringBuilder.write(builder, `*${t.definition.display.description}*`);
StringBuilder.newline(builder);
}
StringBuilder.newline(builder);
@@ -48,7 +48,7 @@ function writeTransformer(t: StateTransformer) {
StringBuilder.write(builder, `\`\`\`js\n${JSON.stringify(ParamDefinition.getDefaultValues(params), null, 2)}\n\`\`\``);
StringBuilder.newline(builder);
}
StringBuilder.write(builder, '----------------------------')
StringBuilder.write(builder, '----------------------------');
StringBuilder.newline(builder);
}
@@ -62,7 +62,7 @@ transformers.forEach(t => {
StringBuilder.newline(builder);
});
StringBuilder.newline(builder);
StringBuilder.write(builder, '----------------------------')
StringBuilder.write(builder, '----------------------------');
StringBuilder.newline(builder);
transformers.forEach(t => writeTransformer(t));

View File

@@ -22,6 +22,7 @@ function paramInfo(param: PD.Any, offset: number): string {
case 'color-list': return `A list of colors as 0xrrggbb`;
case 'vec3': return `3D vector [x, y, z]`;
case 'mat4': return `4x4 transformation matrix`;
case 'url': return `URL couple with unique identifier`;
case 'file': return `JavaScript File Handle`;
case 'file-list': return `JavaScript FileList Handle`;
case 'select': return `One of ${oToS(param.options)}`;

View File

@@ -4,19 +4,19 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as util from 'util'
import * as fs from 'fs'
import fetch from 'node-fetch'
import * as util from 'util';
import * as fs from 'fs';
import fetch from 'node-fetch';
require('util.promisify').shim();
import { CIF } from '../../mol-io/reader/cif'
import { Progress } from '../../mol-task'
import { CIF } from '../../mol-io/reader/cif';
import { Progress } from '../../mol-task';
const readFileAsync = util.promisify(fs.readFile);
async function readFile(path: string) {
if (path.match(/\.bcif$/)) {
const input = await readFileAsync(path)
const input = await readFileAsync(path);
const data = new Uint8Array(input.byteLength);
for (let i = 0; i < input.byteLength; i++) data[i] = input[i];
return data;

View File

@@ -5,11 +5,11 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as argparse from 'argparse'
import * as argparse from 'argparse';
require('util.promisify').shim();
import { CifFrame } from '../../mol-io/reader/cif'
import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing } from '../../mol-model/structure'
import { CifFrame } from '../../mol-io/reader/cif';
import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing } from '../../mol-model/structure';
// import { Run, Progress } from '../../mol-task'
import { OrderedSet } from '../../mol-data/int';
import { openCif, downloadCif } from './helpers';
@@ -32,30 +32,30 @@ export async function readCifFile(path: string) {
}
export function atomLabel(model: Model, aI: number) {
const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
const { label_atom_id } = atoms
const { label_comp_id, label_seq_id } = residues
const { label_asym_id } = chains
const rI = residueAtomSegments.index[aI]
const cI = chainAtomSegments.index[aI]
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)} ${label_atom_id.value(aI)}`
const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy;
const { label_atom_id } = atoms;
const { label_comp_id, label_seq_id } = residues;
const { label_asym_id } = chains;
const rI = residueAtomSegments.index[aI];
const cI = chainAtomSegments.index[aI];
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)} ${label_atom_id.value(aI)}`;
}
export function residueLabel(model: Model, rI: number) {
const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
const { label_comp_id, label_seq_id } = residues
const { label_asym_id } = chains
const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]]
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}`
const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy;
const { label_comp_id, label_seq_id } = residues;
const { label_asym_id } = chains;
const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]];
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}`;
}
export function printSecStructure(model: Model) {
console.log('\nSecondary Structure\n=============');
const { residues } = model.atomicHierarchy;
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
if (!secondaryStructure) return
if (!secondaryStructure) return;
const { key, elements } = secondaryStructure
const { key, elements } = secondaryStructure;
const count = residues._rowCount;
let rI = 0;
while (rI < count) {
@@ -116,7 +116,7 @@ export function printSequence(model: Model) {
const { byEntityKey } = model.sequence;
for (const key of Object.keys(byEntityKey)) {
const { sequence, entityId } = byEntityKey[+key];
const { seqId, compId } = sequence
const { seqId, compId } = sequence;
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
console.log(`${Sequence.getSequenceString(sequence)}`);
}
@@ -132,7 +132,7 @@ export function printRings(structure: Structure) {
for (let i = 0, _i = Math.min(5, all.length); i < _i; i++) {
fps[fps.length] = UnitRing.fingerprint(unit, all[i]);
}
if (all.length > 5) fps.push('...')
if (all.length > 5) fps.push('...');
console.log(`Unit ${unit.id}, ${all.length} ring(s), ${byFingerprint.size} different fingerprint(s).\n ${fps.join(', ')}`);
}
console.log();
@@ -171,8 +171,8 @@ export function printUnits(structure: Structure) {
export function printSymmetryInfo(model: Model) {
console.log('\nSymmetry Info\n=============');
const symmetry = ModelSymmetry.Provider.get(model)
if (!symmetry) return
const symmetry = ModelSymmetry.Provider.get(model);
if (!symmetry) return;
const { size, anglesInRadians } = symmetry.spacegroup.cell;
console.log(`Spacegroup: ${symmetry.spacegroup.name} size: ${Vec3.toString(size)} angles: ${Vec3.toString(anglesInRadians)}`);
console.log(`Assembly names: ${symmetry.assemblies.map(a => a.id).join(', ')}`);
@@ -213,7 +213,7 @@ async function run(frame: CifFrame, args: Args) {
}
async function runDL(pdb: string, args: Args) {
const mmcif = await downloadFromPdb(pdb)
const mmcif = await downloadFromPdb(pdb);
run(mmcif, args);
}
@@ -255,5 +255,5 @@ interface Args {
}
const args: Args = parser.parseArgs();
if (args.download) runDL(args.download, args)
else if (args.file) runFile(args.file, args)
if (args.download) runDL(args.download, args);
else if (args.file) runFile(args.file, args);

View File

@@ -4,13 +4,13 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as fs from 'fs'
import * as argparse from 'argparse'
import * as util from 'util'
import * as fs from 'fs';
import * as argparse from 'argparse';
import * as util from 'util';
import { VolumeData, VolumeIsoValue } from '../../mol-model/volume'
import { downloadCif } from './helpers'
import { CIF } from '../../mol-io/reader/cif'
import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
import { downloadCif } from './helpers';
import { CIF } from '../../mol-io/reader/cif';
import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
import { Table } from '../../mol-data/db';
import { StringBuilder } from '../../mol-util';
@@ -34,9 +34,8 @@ function print(data: Volume) {
const { volume_data_3d_info } = data.source;
const row = Table.getRow(volume_data_3d_info, 0);
console.log(row);
console.log(data.volume.cell);
if (data.volume.transform) console.log(data.volume.transform);
console.log(data.volume.dataStats);
console.log(data.volume.fractionalBox);
}
async function doMesh(data: Volume, filename: string) {

View File

@@ -1,402 +0,0 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateAction, StateBuilder, StateTransformer, State } from '../../../../mol-state';
import { PluginContext } from '../../../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Ingredient, CellPacking } from './data';
import { getFromPdb, getFromCellPackDB } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../../../mol-math/geometry';
import { Task, RuntimeContext } from '../../../../mol-task';
import { StateTransforms } from '../../../../mol-plugin-state/transforms';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../../../mol-script/runtime/query/compiler';
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
import { Column } from '../../../../mol-data/db';
import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
import { AjaxTask } from '../../../../mol-util/data-source';
import { CellPackInfoProvider } from './property';
import { CellPackColorThemeProvider } from './color';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`
}
async function getModel(id: string, baseUrl: string) {
let model: Model;
if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
// return
const cif = await getFromPdb(id)
model = (await trajectoryFromMmCIF(cif).run())[0]
} else {
const pdb = await getFromCellPackDB(id, baseUrl)
model = (await trajectoryFromPDB(pdb).run())[0]
}
return model
}
async function getStructure(model: Model, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model)
const { assembly } = props
if (assembly) {
structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
}
const query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
})
])
const compiled = compile<StructureSelection>(query)
const result = compiled(new QueryContext(structure))
structure = StructureSelection.unionStructure(result)
return structure
}
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
Mat4.transpose(m, m)
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0))
Mat4.setTranslation(m, trans)
return m
}
function getResultTransforms(results: Ingredient['results']) {
return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
}
function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0
const instances: Mat4[] = []
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`
if (!(cname in ingredient)) {
// console.warn(`Expected '${cname}' in ingredient`)
continue
}
const _points = ingredient[cname] as Vec3[]
if (_points.length <= 2) {
// TODO handle curve with 2 or less points
continue
}
const points = new Float32Array(_points.length * 3)
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
const newInstances = getMatFromResamplePoints(points)
instances.push(...newInstances)
}
return instances
}
function getAssembly(transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder()
const { units } = structure;
for (let i = 0, il = transforms.length; i < il; ++i) {
const id = `${i + 1}`
const op = SymmetryOperator.create(id, transforms[i], { assembly: { id, operId: i, operList: [ id ] } })
for (const unit of units) {
builder.addWithOperator(unit, op)
}
}
return builder.getStructure();
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
const { db } = model.sourceData.data
const d = db.atom_site
const n = d._rowCount
const rowCount = n * transforms.length
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
const x = d.Cartn_x.toArray()
const y = d.Cartn_y.toArray()
const z = d.Cartn_z.toArray()
const Cartn_x = new Float32Array(rowCount)
const Cartn_y = new Float32Array(rowCount)
const Cartn_z = new Float32Array(rowCount)
const map = new Uint32Array(rowCount)
const seq = new Int32Array(rowCount)
let offset = 0
for (let c = 0; c < count; ++c) {
const cStart = offsets[c]
const cEnd = offsets[c + 1]
const cLength = cEnd - cStart
for (let t = 0, tl = transforms.length; t < tl; ++t) {
const m = transforms[t]
for (let j = cStart; j < cEnd; ++j) {
const i = offset + j - cStart
const xj = x[j], yj = y[j], zj = z[j]
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
map[i] = j
seq[i] = t + 1
}
offset += cLength
}
}
function multColumn<T>(column: Column<T>) {
const array = column.toArray()
return Column.ofLambda({
value: row => array[map[row]],
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
rowCount, schema: column.schema
})
}
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
auth_seq_id: CifField.ofNumbers(seq),
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
Cartn_x: CifField.ofNumbers(Cartn_x),
Cartn_y: CifField.ofNumbers(Cartn_y),
Cartn_z: CifField.ofNumbers(Cartn_z),
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
id: CifField.ofColumn(Column.ofLambda({
value: row => row,
areValuesEqual: (rowA, rowB) => rowA === rowB,
rowCount, schema: d.id.schema,
})),
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
label_seq_id: CifField.ofNumbers(seq),
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
}
const categories = {
entity: CifCategory.ofTable('entity', db.entity),
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
}
return {
header: name,
categoryNames: Object.keys(categories),
categories
};
}
async function getCurve(name: string, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model)
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = MmcifFormat.fromFrame(cif)
const models = await createModels(format.data.db, format, ctx)
return models[0]
})
const curveModel = await curveModelTask.run()
return getStructure(curveModel)
}
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
const { name, source, results, nbCurve } = ingredient
// TODO can these be added to the library?
if (name === 'HIV1_CAhex_0_1_0') return
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
if (name === 'iLDL') return
if (name === 'peptides') return
if (name === 'lypoglycane') return
if (source.pdb === 'None') return
const model = await getModel(source.pdb || name, baseUrl)
if (!model) return
if (nbCurve) {
return getCurve(name, getCurveTransforms(ingredient), model)
} else {
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
return getAssembly(getResultTransforms(results), structure)
}
}
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing
const structures: Structure[] = []
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName)
const s = await getIngredientStructure(ingredients[iName], baseUrl)
if (s) structures.push(s)
}
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
const builder = Structure.Builder({ label: name })
let offsetInvariantId = 0
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
let maxInvariantId = 0
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId)
}
offsetInvariantId += maxInvariantId + 1
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
const s = builder.getStructure()
for( let i = 0, il = s.models.length; i < il; ++i) {
const { trajectoryInfo } = s.models[i]
trajectoryInfo.size = il
trajectoryInfo.index = i
}
return s
})
}
async function handleHivRna(ctx: { runtime: RuntimeContext, fetch: AjaxTask }, packings: CellPacking[], baseUrl: string) {
for (let i = 0, il = packings.length; i < il; ++i) {
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
const url = `${baseUrl}/extras/rna_allpoints.json`
const data = await ctx.fetch({ url, type: 'string' }).runInContext(ctx.runtime);
const { points } = await (new Response(data)).json() as { points: number[] }
const curve0: Vec3[] = []
for (let j = 0, jl = points.length; j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j))
}
packings[i].ingredients['RNA'] = {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
}
}
}
}
async function loadHivMembrane(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
const membraneBuilder = state.build().toRoot()
.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.StructureFromModel)
await state.updateTree(membraneBuilder).runInContext(runtime)
const membraneParams = {
representation: params.preset.representation,
}
const membrane = state.build().to(membraneBuilder.ref)
await plugin.updateDataState(membrane, { revertOnError: true });
await CellpackMembranePreset.apply(membrane.selector, membraneParams, plugin)
}
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>
if (params.source.name === 'id') {
const url = getCellPackModelUrl(params.source.params, params.baseUrl)
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } })
} else {
const file = params.source.params
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } })
}
const cellPackBuilder = cellPackJson
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack)
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime)
const { packings } = cellPackObject.data
await handleHivRna({ runtime, fetch: plugin.fetch }, packings, params.baseUrl)
for (let i = 0, il = packings.length; i < il; ++i) {
const p = { packing: i, baseUrl: params.baseUrl }
const packing = state.build().to(cellPackBuilder.ref).apply(StructureFromCellpack, p)
await plugin.updateDataState(packing, { revertOnError: true });
const structure = packing.selector.obj?.data
if (structure) {
await CellPackInfoProvider.attach({ fetch: plugin.fetch, runtime }, structure, {
info: { packingsCount: packings.length, packingIndex: i }
})
}
const packingParams = {
traceOnly: params.preset.traceOnly,
representation: params.preset.representation,
}
await CellpackPackingPreset.apply(packing.selector, packingParams, plugin)
}
}
const LoadCellPackModelParams = {
source: PD.MappedStatic('id', {
'id': PD.Select('influenza_model1.json', [
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
['hiv_lipids.bcif', 'hiv_lipids'],
['influenza_model1.json', 'influenza_model1'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
] as const),
'file': PD.File({ accept: 'id' }),
}, { options: [['id', 'Id'], ['file', 'File']] }),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid']))
}, { isExpanded: true })
}
type LoadCellPackModelParams = PD.Values<typeof LoadCellPackModelParams>
export const LoadCellPackModel = StateAction.build({
display: { name: 'Load CellPack', description: 'Open or download a model' },
params: LoadCellPackModelParams,
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
if (!ctx.representation.structure.themes.colorThemeRegistry.has(CellPackColorThemeProvider)) {
ctx.representation.structure.themes.colorThemeRegistry.add(CellPackColorThemeProvider)
}
if (params.source.name === 'id' && params.source.params === 'hiv_lipids.bcif') {
await loadHivMembrane(ctx, taskCtx, state, params)
} else {
await loadPackings(ctx, taskCtx, state, params)
}
}));

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Task } from '../../../../mol-task';
import { CellPack as _CellPack, Cell, CellPacking } from './data';
import { createStructureFromCellPack } from './model';
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/'
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
export { ParseCellPack }
type ParseCellPack = typeof ParseCellPack
const ParseCellPack = PluginStateTransform.BuiltIn({
name: 'parse-cellpack',
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
from: PSO.Format.Json,
to: CellPack
})({
apply({ a }) {
return Task.create('Parse CellPack', async ctx => {
const cell = a.data as Cell
const packings: CellPacking[] = []
const { compartments, cytoplasme } = cell
if (compartments) {
for (const name in compartments) {
const { surface, interior } = compartments[name]
if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients })
if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients })
}
}
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients })
return new CellPack({ cell, packings });
});
}
});
export { StructureFromCellpack }
type StructureFromCellpack = typeof ParseCellPack
const StructureFromCellpack = PluginStateTransform.BuiltIn({
name: 'structure-from-cellpack',
display: { name: 'Structure from CellPack', description: 'Create Structure from CellPack Packing' },
from: CellPack,
to: PSO.Molecule.Structure,
params: a => {
if (!a) {
return {
packing: PD.Numeric(0, {}, { description: 'Packing Index' }),
baseUrl: PD.Text(DefaultCellPackBaseUrl)
};
}
const options = a.data.packings.map((d, i) => [i, d.name] as [number, string])
return {
packing: PD.Select(0, options),
baseUrl: PD.Text(DefaultCellPackBaseUrl)
}
}
})({
apply({ a, params }) {
return Task.create('Structure from CellPack', async ctx => {
const packing = a.data.packings[params.packing]
const structure = await createStructureFromCellPack(packing, params.baseUrl).runInContext(ctx)
return new PSO.Molecule.Structure(structure, { label: packing.name })
});
}
});

View File

@@ -1,48 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CIF } from '../../../../mol-io/reader/cif'
import { parsePDB } from '../../../../mol-io/reader/pdb/parser';
async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data);
const parsed = await comp.run();
if (parsed.isError) throw parsed;
return parsed.result;
}
async function parsePDBfile(data: string, id: string) {
const comp = parsePDB(data, id);
const parsed = await comp.run();
if (parsed.isError) throw parsed;
return parsed.result;
}
async function downloadCif(url: string, isBinary: boolean) {
const data = await fetch(url);
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
}
async function downloadPDB(url: string, id: string) {
const data = await fetch(url);
return parsePDBfile(await data.text(), id);
}
export async function getFromPdb(id: string) {
const parsed = await downloadCif(`https://files.rcsb.org/download/${id}.cif`, false);
return parsed.blocks[0];
}
function getCellPackDataUrl(id: string, baseUrl: string) {
const url = `${baseUrl}/other/${id}`
return url.endsWith('.pdb') ? url : `${url}.pdb`
}
export async function getFromCellPackDB(id: string, baseUrl: string) {
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
return parsed;
}

View File

@@ -1,168 +0,0 @@
// /**
// * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
// *
// * @author David Sehnal <david.sehnal@gmail.com>
// */
// import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
// import { StateTransforms } from '../../../mol-plugin/state/transforms';
// import { createModelTree } from '../../../mol-plugin/state/actions/structure';
// import { PluginContext } from '../../../mol-plugin/context';
// import { PluginStateObject } from '../../../mol-plugin/state/objects';
// import { ParamDefinition } from '../../../mol-util/param-definition';
// import { PluginCommands } from '../../../mol-plugin/command';
// import { Vec3 } from '../../../mol-math/linear-algebra';
// import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
// import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
// import { Text } from '../../../mol-geo/geometry/text/text';
// import { UUID } from '../../../mol-util';
// import { ColorNames } from '../../../mol-util/color/names';
// import { Camera } from '../../../mol-canvas3d/camera';
// import { createStructureRepresentation3dParamss } from '../../../mol-plugin/state/transforms/representation';
// import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
// export const CreateJoleculeState = StateAction.build({
// display: { name: 'Jolecule State Import' },
// params: { id: ParamDefinition.Text('1mbo') },
// from: PluginStateObject.Root
// })(async ({ ref, state, params }, plugin: PluginContext) => {
// try {
// const id = params.id.trim().toLowerCase();
// const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
// data.sort((a, b) => a.order - b.order);
// await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
// plugin.state.snapshots.clear();
// const template = createTemplate(plugin, state, id);
// const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
// for (const s of snapshots) {
// plugin.state.snapshots.add(s);
// }
// PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
// } catch (e) {
// plugin.log.error(`Jolecule Failed: ${e}`);
// }
// });
// interface JoleculeSnapshot {
// order: number,
// distances: { i_atom1: number, i_atom2: number }[],
// labels: { i_atom: number, text: string }[],
// camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
// selected: number[],
// text: string
// }
// function createTemplate(plugin: PluginContext, state: State, id: string) {
// const b = new StateBuilder.Root(state.tree);
// const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
// const model = createModelTree(data, 'cif');
// const structure = model.apply(StateTransforms.Model.StructureFromModel);
// createDefaultStructureComplex(plugin, structure);
// return { tree: b.getTree(), structure: structure.ref };
// }
// const labelOptions: ParamDefinition.Values<Text.Params> = {
// ...ParamDefinition.getDefaultValues(Text.Params),
// tether: true,
// sizeFactor: 1.3,
// attachment: 'bottom-right',
// offsetZ: 10,
// background: true,
// backgroundMargin: 0.2,
// backgroundColor: ColorNames.skyblue,
// backgroundOpacity: 0.9
// }
// // const distanceLabelOptions = {
// // ...ParamDefinition.getDefaultValues(Text.Params),
// // sizeFactor: 1,
// // offsetX: 0,
// // offsetY: 0,
// // offsetZ: 10,
// // background: true,
// // backgroundMargin: 0.2,
// // backgroundColor: ColorNames.snow,
// // backgroundOpacity: 0.9
// // }
// function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
// const b = new StateBuilder.Root(template.tree);
// let i = 0;
// for (const l of params.e.labels) {
// const expression = createExpression([l.i_atom]);
// const group = b.to(template.structure)
// .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
// group
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
// .apply(StateTransforms.Representation.StructureLabels3D, {
// target: { name: 'static-text', params: { value: l.text || '' } },
// options: labelOptions
// });
// group
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
// .apply(StateTransforms.Representation.StructureRepresentation3D,
// createStructureRepresentation3dParamss.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
// }
// if (params.e.selected && params.e.selected.length > 0) {
// b.to(template.structure)
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
// .apply(StateTransforms.Representation.StructureRepresentation3D,
// createStructureRepresentation3dParamss.getDefaultParamsStatic(plugin, 'ball-and-stick'));
// }
// // TODO
// // for (const l of params.e.distances) {
// // b.to('structure')
// // .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
// // .apply(StateTransforms.Representation.StructureLabels3D, {
// // target: { name: 'static-text', params: { value: l. || '' } },
// // options: labelOptions
// // });
// // }
// return PluginStateSnapshotManager.Entry({
// id: UUID.create22(),
// data: { tree: StateTree.toJSON(b.getTree()) },
// camera: {
// current: getCameraSnapshot(params.e.camera),
// transitionStyle: 'animate',
// transitionDurationInMs: 350
// }
// }, {
// name: params.e.text
// });
// }
// function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
// const direction = Vec3.sub(Vec3(), e.pos, e.in);
// Vec3.normalize(direction, direction);
// const up = Vec3.sub(Vec3(), e.pos, e.up);
// Vec3.normalize(up, up);
// const s: Camera.Snapshot = {
// mode: 'perspective',
// fov: Math.PI / 4,
// position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
// target: e.pos,
// radius: (e.slab.z_back - e.slab.z_front) / 2,
// fog: 50,
// up,
// };
// return s;
// }
// function createExpression(atomIndices: number[]) {
// if (atomIndices.length === 0) return MS.struct.generator.empty();
// return MS.struct.generator.atomGroups({
// 'atom-test': atomIndices.length === 1
// ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
// : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
// 'group-by': 0
// });
// }

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -7,16 +7,18 @@
import '../../mol-util/polyfill';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import './favicon.ico'
import './index.html';
import './favicon.ico';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginSpec } from '../../mol-plugin/spec';
import { LoadCellPackModel } from './extensions/cellpack/model';
import { StructureFromCellpack } from './extensions/cellpack/state';
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
import { PluginConfig } from '../../mol-plugin/config';
require('mol-plugin-ui/skin/light.scss')
import { CellPack } from '../../extensions/cellpack';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { Asset } from '../../mol-util/assets';
require('mol-plugin-ui/skin/light.scss');
function getParam(name: string, regex: string): string {
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
@@ -27,13 +29,14 @@ const hideControls = getParam('hide-controls', `[^&]+`) === '1';
function init() {
const spec: PluginSpec = {
actions: [
...DefaultPluginSpec.actions,
// PluginSpec.Action(CreateJoleculeState),
PluginSpec.Action(LoadCellPackModel),
PluginSpec.Action(StructureFromCellpack),
actions: [...DefaultPluginSpec.actions],
behaviors: [
...DefaultPluginSpec.behaviors,
PluginSpec.Behavior(CellPack),
PluginSpec.Behavior(PDBeStructureQualityReport),
PluginSpec.Behavior(RCSBAssemblySymmetry),
PluginSpec.Behavior(RCSBValidationReport),
],
behaviors: [...DefaultPluginSpec.behaviors],
animations: [...DefaultPluginSpec.animations || []],
customParamEditors: DefaultPluginSpec.customParamEditors,
layout: {
@@ -62,7 +65,7 @@ async function trySetSnapshot(ctx: PluginContext) {
const url = snapshotId
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
: snapshotUrl;
await PluginCommands.State.Snapshots.Fetch(ctx, { url })
await PluginCommands.State.Snapshots.Fetch(ctx, { url });
} catch (e) {
ctx.log.error('Failed to load snapshot.');
console.warn('Failed to load snapshot', e);
@@ -86,7 +89,7 @@ async function tryLoadFromUrl(ctx: PluginContext) {
source: {
name: 'url',
params: {
url,
url: Asset.Url(url),
format: format as any,
isBinary,
options: params.source.params.options,

View File

@@ -18,13 +18,13 @@ export const StripedResidues = CustomElementProperty.create<number>({
for (let i = 0, _i = model.atomicHierarchy.atoms._rowCount; i < _i; i++) {
map.set(i as ElementIndex, residueIndex[i] % 2);
}
return map;
return { value: map };
},
coloring: {
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff); },
defaultColor: Color(0x777777)
},
getLabel(e) {
return e === 0 ? 'Odd stripe' : 'Even stripe'
return e === 0 ? 'Odd stripe' : 'Even stripe';
}
})
});

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import * as React from 'react';
export class CustomToastMessage extends PluginUIComponent {
render() {
return <>
Custom <i>Toast</i> content. No timeout.
</>;
}
}

View File

@@ -50,7 +50,7 @@
<input type='text' id='url' placeholder='url' />
<input type='text' id='assemblyId' placeholder='assembly id' />
<select id='format'>
<option value='cif' selected>CIF</option>
<option value='mmcif' selected>mmCIF</option>
<option value='pdb'>PDB</option>
</select>
</div>
@@ -60,7 +60,7 @@
var pdbId = '1grm', assemblyId= '1';
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
var format = 'cif';
var format = 'mmcif';
$('url').value = url;
$('url').onchange = function (e) { url = e.target.value; }
@@ -104,6 +104,7 @@
addHeader('Misc');
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
addHeader('Interactivity');
addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { EmptyLoci } from '../../mol-model/loci';
import { StructureSelection } from '../../mol-model/structure';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { Script } from '../../mol-script/script';
import { Color } from '../../mol-util/color';
import { StripedResidues } from './coloring';
import { CustomToastMessage } from './controls';
import './index.html';
import { buildStaticSuperposition, dynamicSuperpositionTest, StaticSuperpositionTestData } from './superposition';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { Asset } from '../../mol-util/assets';
require('mol-plugin-ui/skin/light.scss');
type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
class BasicWrapper {
plugin: PluginContext;
init(target: string | HTMLElement) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec,
layout: {
initial: {
isExpanded: false,
showControls: false
},
controls: {
// left: 'none'
}
},
components: {
remoteState: 'none'
}
});
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {
await this.plugin.clear();
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
structure: assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } },
showUnitcell: false,
representationPreset: 'auto'
});
}
setBackground(color: number) {
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: props => { props.renderer.backgroundColor = Color(color); } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: props => {
props.trackball.spin = !props.trackball.spin;
}
});
if (!this.plugin.canvas3d.props.trackball.spin) PluginCommands.Camera.Reset(this.plugin, {});
}
animate = {
modelIndex: {
maxFPS: 8,
onceForward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }); },
onceBackward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }); },
palindrome: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }); },
loop: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }); },
stop: () => this.plugin.managers.animation.stop()
}
}
coloring = {
applyStripes: async () => {
this.plugin.dataTransaction(async () => {
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: StripedResidues.propertyProvider.descriptor.name as any });
}
});
},
applyDefault: async () => {
this.plugin.dataTransaction(async () => {
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: 'default' });
}
});
}
}
interactivity = {
highlightOn: () => {
const seq_id = 7;
const data = (this.plugin.state.data.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
'group-by': Q.struct.atomProperty.macromolecular.residueKey()
}), data);
const loci = StructureSelection.toLociWithSourceUnits(sel);
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
},
clearHighlight: () => {
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
tests = {
staticSuperposition: async () => {
await this.plugin.clear();
return buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
},
dynamicSuperposition: async () => {
await this.plugin.clear();
return dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
},
toggleValidationTooltip: () => {
return this.plugin.state.updateBehavior(PDBeStructureQualityReport, params => { params.showTooltip = !params.showTooltip; });
},
showToasts: () => {
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 1',
message: 'This is an example text, timeout 3s',
key: 'toast-1',
timeoutMs: 3000
});
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 2',
message: CustomToastMessage,
key: 'toast-2'
});
},
hideToasts: () => {
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
}
}
}
(window as any).BasicMolStarWrapper = new BasicWrapper();

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Mat4 } from '../../mol-math/linear-algebra';
import { QueryContext, StructureSelection } from '../../mol-model/structure';
import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import Expression from '../../mol-script/language/expression';
import { compile } from '../../mol-script/runtime/query/compiler';
import { StateObjectRef } from '../../mol-state';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Asset } from '../../mol-util/assets';
export type SuperpositionTestInput = {
pdbId: string,
auth_asym_id: string,
matrix: Mat4
}[];
export function buildStaticSuperposition(plugin: PluginContext, src: SuperpositionTestInput) {
return plugin.dataTransaction(async () => {
for (const s of src) {
const { structure } = await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`, 'mmcif');
await transform(plugin, structure, s.matrix);
const chain = await plugin.builders.structure.tryCreateComponentFromExpression(structure, chainSelection(s.auth_asym_id), `Chain ${s.auth_asym_id}`);
if (chain) await plugin.builders.structure.representation.addRepresentation(chain, { type: 'cartoon' });
}
});
}
export const StaticSuperpositionTestData: SuperpositionTestInput = [
{
pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity()
},
{
pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
[0.406, 0.879, 0.248, -200.633],
[0.693, -0.473, 0.544, 73.403],
[0.596, -0.049, -0.802, -14.209],
[0, 0, 0, 1]])
},
{
pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
[-0.053, -0.077, 0.996, -45.633],
[-0.312, 0.949, 0.057, -12.255],
[-0.949, -0.307, -0.074, 53.562],
[0, 0, 0, 1]])
}
];
export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], comp_id: string) {
return plugin.dataTransaction(async () => {
for (const s of src) {
await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`, 'mmcif');
}
const pivot = MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
})
]);
const rest = MS.struct.modifier.exceptBy({
0: MS.struct.modifier.includeSurroundings({
0: pivot,
radius: 5
}),
by: pivot
});
const query = compile<StructureSelection>(pivot);
const xs = plugin.managers.structure.hierarchy.current.structures;
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.cell.obj!.data))));
const transforms = superposeStructures(selections);
await siteVisual(plugin, xs[0].cell, pivot, rest);
for (let i = 1; i < selections.length; i++) {
await transform(plugin, xs[i].cell, transforms[i - 1].bTransform);
await siteVisual(plugin, xs[i].cell, pivot, rest);
}
});
}
async function siteVisual(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
const center = await plugin.builders.structure.tryCreateComponentFromExpression(s, pivot, 'pivot');
if (center) await plugin.builders.structure.representation.addRepresentation(center, { type: 'ball-and-stick', color: 'residue-name' });
const surr = await plugin.builders.structure.tryCreateComponentFromExpression(s, rest, 'rest');
if (surr) await plugin.builders.structure.representation.addRepresentation(surr, { type: 'ball-and-stick', color: 'uniform', size: 'uniform', sizeParams: { value: 0.33 } });
}
async function loadStructure(plugin: PluginContext, url: string, format: BuiltInTrajectoryFormat, assemblyId?: string) {
const data = await plugin.builders.data.download({ url: Asset.Url(url) });
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
const model = await plugin.builders.structure.createModel(trajectory);
const structure = await plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : void 0);
return { data, trajectory, model, structure };
}
function chainSelection(auth_asym_id: string) {
return MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
});
}
function transform(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, matrix: Mat4) {
const b = plugin.state.data.build().to(s)
.insert(StateTransforms.Model.TransformStructureConformation, { transform: { name: 'matrix', params: { data: matrix, transpose: false } } });
return plugin.runTask(plugin.state.data.updateTree(b));
}

View File

@@ -4,9 +4,9 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Table } from '../../mol-data/db'
import { CifWriter } from '../../mol-io/writer/cif'
import * as S from './schemas'
import { Table } from '../../mol-data/db';
import { CifWriter } from '../../mol-io/writer/cif';
import * as S from './schemas';
// import { getCategoryInstanceProvider } from './utils'
export default function create(allData: any) {

View File

@@ -4,21 +4,21 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Column } from '../../mol-data/db'
import { Column } from '../../mol-data/db';
import Type = Column.Schema
export const Sources = {
id: Type.str,
count: Type.int
}
};
export type Sources = typeof Sources
export const Base = {
id: Type.str,
identifier: Type.str,
mapping_group_id: Type.int
}
};
export type Base = typeof Base
export const mapping = {
@@ -36,17 +36,17 @@ export const mapping = {
end_label_seq_id: Type.int,
end_auth_seq_id: Type.int,
pdbx_end_PDB_ins_code: Type.str
}
};
export type mapping = typeof mapping
export const Pfam = {
description: Type.str
}
};
export type Pfam = typeof Pfam
export const InterPro = {
name: Type.str
}
};
export type InterPro = typeof InterPro
export const CATH = {
@@ -56,32 +56,32 @@ export const CATH = {
identifier: Type.str,
class: Type.str,
topology: Type.str,
}
};
export type CATH = typeof CATH
export const EC = {
accepted_name: Type.str,
reaction: Type.str,
systematic_name: Type.str
}
};
export type EC = typeof EC
export const UniProt = {
name: Type.str
}
};
export type UniProt = typeof UniProt
export const SCOP = {
sccs: Type.str,
description: Type.str
}
};
export type SCOP = typeof SCOP
export const GO = {
category: Type.str,
definition: Type.str,
name: Type.str
}
};
export type GO = typeof GO
export const categories = {
@@ -92,4 +92,4 @@ export const categories = {
UniProt,
SCOP,
GO
}
};

View File

@@ -4,9 +4,9 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as express from 'express'
import fetch from 'node-fetch'
import createMapping from './mapping'
import express from 'express';
import fetch from 'node-fetch';
import createMapping from './mapping';
async function getMappings(id: string) {
const data = await fetch(`https://www.ebi.ac.uk/pdbe/api/mappings/${id}`);
@@ -19,7 +19,7 @@ let PORT = process.env.port || 1338;
const app = express();
const PREFIX = '/'
const PREFIX = '/';
app.get(`${PREFIX}/:id`, async (req, res) => {
try {
@@ -41,7 +41,7 @@ app.get(`${PREFIX}/:id`, async (req, res) => {
app.get(`${PREFIX}`, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Usage: /pdb_id, e.g. /1tqn');
})
});
app.listen(PORT);

View File

@@ -4,8 +4,8 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import fetch from 'node-fetch'
import createMapping from './mapping'
import fetch from 'node-fetch';
import createMapping from './mapping';
(async function () {
const data = await fetch('https://www.ebi.ac.uk/pdbe/api/mappings/1tqn?pretty=true');

View File

@@ -0,0 +1,118 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import './index.html';
import { Asset } from '../../mol-util/assets';
require('mol-plugin-ui/skin/light.scss');
type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
type _Preset = Pick<Canvas3DProps, 'multiSample' | 'postprocessing' | 'renderer'>
type Preset = { [K in keyof _Preset]: Partial<_Preset[K]> }
const Canvas3DPresets = {
illustrative: <Preset> {
multiSample: {
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusion: { name: 'on', params: { bias: 0.8, kernelSize: 6, radius: 64 } },
outline: { name: 'on', params: { scale: 1, threshold: 0.8 } }
},
renderer: {
ambientIntensity: 1,
lightIntensity: 0,
}
},
occlusion: <Preset> {
multiSample: {
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusion: { name: 'on', params: { bias: 0.8, kernelSize: 6, radius: 64 } },
outline: { name: 'off', params: { } }
},
renderer: {
ambientIntensity: 0.4,
lightIntensity: 0.6,
}
},
standard: <Preset> {
multiSample: {
mode: 'off' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusion: { name: 'off', params: { } },
outline: { name: 'off', params: { } }
},
renderer: {
ambientIntensity: 0.4,
lightIntensity: 0.6,
}
}
};
type Canvas3DPreset = keyof typeof Canvas3DPresets
class LightingDemo {
plugin: PluginContext;
init(target: string | HTMLElement) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec,
layout: {
initial: {
isExpanded: false,
showControls: false
},
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
}
});
this.setPreset('illustrative');
}
setPreset(preset: Canvas3DPreset) {
const props = Canvas3DPresets[preset];
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
...props,
multiSample: {
...this.plugin.canvas3d!.props.multiSample,
...props.multiSample
},
renderer: {
...this.plugin.canvas3d!.props.renderer,
...props.renderer
},
postprocessing: {
...this.plugin.canvas3d!.props.postprocessing,
...props.postprocessing
},
}});
}
async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {
await this.plugin.clear();
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
const model = await this.plugin.builders.structure.createModel(trajectory);
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } });
const polymer = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer');
if (polymer) await this.plugin.builders.structure.representation.addRepresentation(polymer, { type: 'spacefill', color: 'illustrative' });
const ligand = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand');
if (ligand) await this.plugin.builders.structure.representation.addRepresentation(ligand, { type: 'ball-and-stick' });
}
}
(window as any).LightingDemo = new LightingDemo();

View File

@@ -9,6 +9,7 @@ import { CustomElementProperty } from '../../mol-model-props/common/custom-eleme
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure';
import { Color } from '../../mol-util/color';
import { CustomProperty } from '../../mol-model-props/common/custom-property';
import { Asset } from '../../mol-util/assets';
const EvolutionaryConservationPalette: Color[] = [
[255, 255, 129], // insufficient
@@ -30,9 +31,9 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
type: 'static',
async getData(model: Model, ctx: CustomProperty.Context) {
const id = model.entryId.toLowerCase();
const url = `https://proteopedia.org/cgi-bin/cnsrf?${id}`
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
const annotations = (json && json.residueAnnotations) || [];
const url = Asset.getUrlAsset(ctx.assetManager, `https://proteopedia.org/cgi-bin/cnsrf?${id}`);
const json = await ctx.assetManager.resolve(url, 'json').runInContext(ctx.runtime);
const annotations = json.data?.residueAnnotations || [];
const conservationMap = new Map<string, number>();
@@ -58,7 +59,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
}
}
return map;
return { value: map, assets: [json] };
},
coloring: {
getColor(e: number) {
@@ -68,7 +69,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
defaultColor: EvolutionaryConservationDefaultColor
},
getLabel(e) {
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
if (e === 10) return `Evolutionary Conservation: Insufficient Data`;
return e ? `Evolutionary Conservation: ${e}` : void 0;
}
});

View File

@@ -11,58 +11,58 @@ import { Unit, StructureProperties, StructureElement, Bond } from '../../mol-mod
import { Color } from '../../mol-util/color';
import { Location } from '../../mol-model/location';
import { ColorTheme, LocationColor } from '../../mol-theme/color';
import { ParamDefinition as PD } from '../../mol-util/param-definition'
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ThemeDataContext } from '../../mol-theme/theme';
import { Column } from '../../mol-data/db';
const Description = 'Gives every chain a color from a list based on its `asym_id` value.'
const Description = 'Gives every chain a color from a list based on its `asym_id` value.';
export function createProteopediaCustomTheme(colors: number[]) {
const ProteopediaCustomColorThemeParams = {
colors: PD.ObjectList({ color: PD.Color(Color(0xffffff)) }, ({ color }) => Color.toHexString(color),
{ defaultValue: colors.map(c => ({ color: Color(c) })) })
}
};
type ProteopediaCustomColorThemeParams = typeof ProteopediaCustomColorThemeParams
function getChainIdColorThemeParams(ctx: ThemeDataContext) {
return ProteopediaCustomColorThemeParams // TODO return copy
return ProteopediaCustomColorThemeParams; // TODO return copy
}
function getAsymId(unit: Unit): StructureElement.Property<string> {
switch (unit.kind) {
case Unit.Kind.Atomic:
return StructureProperties.chain.label_asym_id
return StructureProperties.chain.label_asym_id;
case Unit.Kind.Spheres:
case Unit.Kind.Gaussians:
return StructureProperties.coarse.asym_id
return StructureProperties.coarse.asym_id;
}
}
function addAsymIds(map: Map<string, number>, data: Column<string>) {
let j = map.size
let j = map.size;
for (let o = 0, ol = data.rowCount; o < ol; ++o) {
const k = data.value(o)
const k = data.value(o);
if (!map.has(k)) {
map.set(k, j)
j += 1
map.set(k, j);
j += 1;
}
}
}
function ProteopediaCustomColorTheme(ctx: ThemeDataContext, props: PD.Values<ProteopediaCustomColorThemeParams>): ColorTheme<ProteopediaCustomColorThemeParams> {
let color: LocationColor
let color: LocationColor;
const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
if (ctx.structure) {
const l = StructureElement.Location.create(ctx.structure)
const { models } = ctx.structure
const asymIdSerialMap = new Map<string, number>()
const l = StructureElement.Location.create(ctx.structure);
const { models } = ctx.structure;
const asymIdSerialMap = new Map<string, number>();
for (let i = 0, il = models.length; i < il; ++i) {
const m = models[i]
addAsymIds(asymIdSerialMap, m.atomicHierarchy.chains.label_asym_id)
const m = models[i];
addAsymIds(asymIdSerialMap, m.atomicHierarchy.chains.label_asym_id);
if (m.coarseHierarchy.isDefined) {
addAsymIds(asymIdSerialMap, m.coarseHierarchy.spheres.asym_id)
addAsymIds(asymIdSerialMap, m.coarseHierarchy.gaussians.asym_id)
addAsymIds(asymIdSerialMap, m.coarseHierarchy.spheres.asym_id);
addAsymIds(asymIdSerialMap, m.coarseHierarchy.gaussians.asym_id);
}
}
@@ -72,16 +72,16 @@ export function createProteopediaCustomTheme(colors: number[]) {
const o = asymIdSerialMap.get(asym_id(location)) || 0;
return colors[o % colorCount].color;
} else if (Bond.isLocation(location)) {
const asym_id = getAsymId(location.aUnit)
l.unit = location.aUnit
l.element = location.aUnit.elements[location.aIndex]
const asym_id = getAsymId(location.aUnit);
l.unit = location.aUnit;
l.element = location.aUnit.elements[location.aIndex];
const o = asymIdSerialMap.get(asym_id(l)) || 0;
return colors[o % colorCount].color;
}
return defaultColor
}
return defaultColor;
};
} else {
color = () => defaultColor
color = () => defaultColor;
}
return {
@@ -91,7 +91,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
props,
description: Description,
legend: undefined
}
};
}
return {
@@ -102,5 +102,5 @@ export function createProteopediaCustomTheme(colors: number[]) {
getParams: getChainIdColorThemeParams,
defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
}
};
}

View File

@@ -4,12 +4,11 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ResidueIndex, Model } from '../../mol-model/structure';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
import { Model, ResidueIndex } from '../../mol-model/structure';
import { PluginContext } from '../../mol-plugin/context';
import { StructureRepresentationRegistry } from '../../mol-repr/structure/registry';
import { ColorTheme } from '../../mol-theme/color';
import { PolymerType } from '../../mol-model/structure/model/types';
import { PluginContext } from '../../mol-plugin/context';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
export interface ModelInfo {
hetResidues: { name: string, indices: ResidueIndex[] }[],
@@ -54,11 +53,10 @@ export namespace ModelInfo {
const hetMap = new Map<string, ModelInfo['hetResidues'][0]>();
for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
if (model.atomicHierarchy.derived.residue.polymerType[rI] !== PolymerType.NA) continue;
const cI = chainIndex[residueOffsets[rI]];
const eI = model.atomicHierarchy.index.getEntityFromChain(cI);
if (model.entities.data.type.value(eI) === 'water') continue;
const entityType = model.entities.data.type.value(eI);
if (entityType !== 'non-polymer' && entityType !== 'branched') continue;
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
@@ -72,7 +70,7 @@ export namespace ModelInfo {
}
const preferredAssemblyId = await pref;
const symmetry = ModelSymmetry.Provider.get(model)
const symmetry = ModelSymmetry.Provider.get(model);
return {
hetResidues: hetResidues,
@@ -86,6 +84,7 @@ export type SupportedFormats = 'cif' | 'pdb'
export interface LoadParams {
url: string,
format?: SupportedFormats,
isBinary?: boolean,
assemblyId?: string,
representationStyle?: RepresentationStyle
}

View File

@@ -60,6 +60,7 @@
<option value='cif' selected>CIF</option>
<option value='pdb'>PDB</option>
</select>
<input type='checkbox' id='isBinary' style="display: inline-block; width: auto" /> <label for="isBinary"> Binary</label><br />
</div>
<div id="app"></div>
<div id="volume-streaming-wrapper"></div>
@@ -74,8 +75,8 @@
function $(id) { return document.getElementById(id); }
var pdbId = '1cbs', assemblyId= 'preferred';
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
var pdbId = '1cbs', assemblyId= 'preferred', isBinary = true;
var url = 'https://www.ebi.ac.uk/pdbe/entry-files/download/' + pdbId + '.bcif'
var format = 'cif';
$('url').value = url;
@@ -84,13 +85,15 @@
$('assemblyId').onchange = function (e) { assemblyId = e.target.value; }
$('format').value = format;
$('format').onchange = function (e) { format = e.target.value; }
$('isBinary').checked = isBinary;
$('isBinary').onchange = function (e) { isBinary = !!e.target.checked; };
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
// var format = 'pdb';
// var assemblyId = 'deposited';
var representationStyle = {
sequence: { coloring: 'proteopedia-custom' }, // or just { }
// sequence: { coloring: 'proteopedia-custom' }, // or just { }
hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
water: { hide: true },
snfg3d: { hide: false }
@@ -100,7 +103,7 @@
customColorList: CustomColors
});
PluginWrapper.setBackground(0xffffff);
PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.load({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.toggleSpin();
PluginWrapper.events.modelInfo.subscribe(function (info) {
@@ -108,8 +111,8 @@
listHetGroups(info);
});
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format }));
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format, isBinary }));
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, isBinary, assemblyId: assemblyId }));
addSeparator();
@@ -173,16 +176,19 @@
var snapshot;
addControl('Set Snapshot', () => {
snapshot = PluginWrapper.snapshot.get();
// could use JSON.stringify(snapshot) and upload the data
// const options = { data: true, behavior: false, animation: false, interactivity: false, canvas3d: false, camera: false, cameraTransition: false };
snapshot = PluginWrapper.plugin.state.getSnapshot(/** options */);
// console.log(JSON.stringify(snapshot, null, 2));
});
addControl('Restore Snapshot', () => {
if (!snapshot) return;
PluginWrapper.snapshot.set(snapshot);
});
addControl('Download Snapshot', () => {
snapshot = PluginWrapper.snapshot.download();
addControl('Download State', () => {
snapshot = PluginWrapper.snapshot.download('molj');
});
addControl('Download Session', () => {
snapshot = PluginWrapper.snapshot.download('molx');
});
////////////////////////////////////////////////////////
@@ -218,7 +224,7 @@
var l = document.createElement('button');
l.innerText = r.name;
l.onclick = function () {
PluginWrapper.hetGroups.focusFirst(r.name);
PluginWrapper.hetGroups.focusFirst(r.name, { doNotLabelWaters: true });
};
div.appendChild(l);
});

View File

@@ -6,7 +6,7 @@
import * as ReactDOM from 'react-dom';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import './index.html';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
@@ -28,11 +28,12 @@ import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
import { download } from '../../mol-util/download';
import { getFormattedTime } from '../../mol-util/date';
require('../../mol-plugin-ui/skin/light.scss')
import { Asset } from '../../mol-util/assets';
require('../../mol-plugin-ui/skin/light.scss');
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 5;
static VERSION_MINOR = 0;
static VERSION_MINOR = 4;
private _ev = RxEventHelper.create();
@@ -73,8 +74,8 @@ class MolStarProteopediaWrapper {
return this.plugin.state.data;
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
private download(b: StateBuilder.To<PSO.Root>, url: string, isBinary: boolean) {
return b.apply(StateTransforms.Data.Download, { url: Asset.Url(url), isBinary });
}
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats) {
@@ -93,7 +94,7 @@ class MolStarProteopediaWrapper {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
};
const s = model
.apply(StateTransforms.Model.StructureFromModel, props, { ref: StateElements.Assembly });
@@ -185,7 +186,7 @@ class MolStarProteopediaWrapper {
const model = this.getObj<PluginStateObject.Molecule.Model>('model');
if (!model) return;
const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly)
const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly);
this.events.modelInfo.next(info);
return info;
}
@@ -194,8 +195,9 @@ class MolStarProteopediaWrapper {
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = 'deposited', representationStyle }: LoadParams) {
private emptyLoadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
private loadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
async load({ url, format = 'cif', assemblyId = 'deposited', isBinary = false, representationStyle }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.data;
@@ -208,7 +210,7 @@ class MolStarProteopediaWrapper {
if (loadType === 'full') {
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
const modelTree = this.model(this.download(state.build().toRoot(), url, isBinary), format);
await this.applyState(modelTree);
const info = await this.doInfo(true);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
@@ -223,7 +225,7 @@ class MolStarProteopediaWrapper {
name: 'assembly' as const,
params: { id: asmId || 'deposited' }
}
}
};
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
await this.applyState(tree);
}
@@ -264,36 +266,17 @@ class MolStarProteopediaWrapper {
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { }),
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
// if (!options) {
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: {
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
// clip: DefaultCanvas3DParams.clip
// }
// });
// return;
// }
// options = options || { };
// const props = this.plugin.canvas3d.props;
// const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
// const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
// });
// }
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { })
}
animate = {
modelIndex: {
maxFPS: 8,
onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
stop: () => this.plugin.state.animation.stop()
onceForward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }); },
onceBackward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }); },
palindrome: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }); },
loop: () => { this.plugin.managers.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }); },
stop: () => this.plugin.managers.animation.stop()
}
}
@@ -304,11 +287,6 @@ class MolStarProteopediaWrapper {
}
const state = this.state;
// const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
// for (const v of visuals) {
// }
const tree = state.build();
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
@@ -353,12 +331,10 @@ class MolStarProteopediaWrapper {
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset(this.plugin, { });
},
focusFirst: async (compId: string) => {
focusFirst: async (compId: string, options?: { hideLabels: boolean, doNotLabelWaters: boolean }) => {
if (!this.state.transforms.has(StateElements.Assembly)) return;
await PluginCommands.Camera.Reset(this.plugin, { });
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
const update = this.state.build();
update.delete(StateElements.HetGroupFocusGroup);
@@ -372,69 +348,66 @@ class MolStarProteopediaWrapper {
const surroundings = MS.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true });
const group = update.to(StateElements.Assembly).group(StateTransforms.Misc.CreateGroup, { label: compId }, { ref: StateElements.HetGroupFocusGroup });
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
const coreSel = group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Core', expression: core }, { ref: StateElements.HetGroupFocus });
coreSel.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick'
}));
coreSel.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
type: 'label',
typeParams: { level: 'element' }
}), { tags: ['proteopedia-labels'] });
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Core', expression: core }, { ref: StateElements.HetGroupFocus })
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createCoreVisualParams());
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Surroundings', expression: surroundings })
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
// sel.apply(StateTransforms.Representation.StructureLabels3D, {
// target: { name: 'residues', params: { } },
// options: {
// ...ParamDefinition.getDefaultValues(Text.Params),
// background: true,
// backgroundMargin: 0.2,
// backgroundColor: ColorNames.snow,
// backgroundOpacity: 0.9,
// }
// });
.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick',
color: 'uniform', colorParams: { value: ColorNames.gray },
size: 'uniform', sizeParams: { value: 0.33 }
}));
if (!options?.hideLabels) {
// Labels
const waters = MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.entityType(), 'water']),
});
const exclude = options?.doNotLabelWaters ? MS.struct.combinator.merge([core, waters]) : core;
const onlySurroundings = MS.struct.modifier.exceptBy({ 0: surroundings, by: exclude });
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Surroundings (only)', expression: onlySurroundings })
.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
type: 'label',
typeParams: { level: 'residue' }
}), { tags: ['proteopedia-labels'] }); // the tag can later be used to toggle the labels
}
await PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
const focus = (this.state.select(StateElements.HetGroupFocus)[0].obj as PluginStateObject.Molecule.Structure).data;
const sphere = focus.boundary.sphere;
// const asmCenter = asm.boundary.sphere.center;
// const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
// Vec3.normalize(position, position);
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
const radius = Math.max(sphere.radius, 5)
const radius = Math.max(sphere.radius, 5);
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius);
PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: 250 });
}
}
private createSurVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick',
color: 'uniform', colorParams: { value: ColorNames.gray },
size: 'uniform', sizeParams: { value: 0.33 }
});
}
private createCoreVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick'
});
}
snapshot = {
get: () => {
return this.plugin.state.getSnapshot();
get: (params?: PluginState.SnapshotParams) => {
return this.plugin.state.getSnapshot(params);
},
set: (snapshot: PluginState.Snapshot) => {
return this.plugin.state.setSnapshot(snapshot);
},
download: () => {
const json = JSON.stringify(this.plugin.state.getSnapshot(), null, 2);
const blob = new Blob([json], {type : 'application/json;charset=utf-8'});
download(blob, `mol-star_state_${(name || getFormattedTime())}.json`)
download: async (type: 'molj' | 'molx' = 'molj', params?: PluginState.SnapshotParams) => {
const data = await this.plugin.managers.snapshot.serialize({ type, params });
download(data, `mol-star_state_${(name || getFormattedTime())}.${type}`);
},
fetch: async (url: string) => {
fetch: async (url: string, type: 'molj' | 'molx' = 'molj') => {
try {
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
// TODO: is this OK to test for snapshots from server?
await this.plugin.state.setSnapshot(snapshot?.data?.entries?.[0]?.snapshot || snapshot);
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
this.loadedParams = { ...this.emptyLoadedParams };
await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
} catch (e) {
console.log(e);
}

View File

@@ -13,7 +13,6 @@ import { StateElements } from '../helpers';
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={plugin}>
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
</PluginContextContainer>,
parent);
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
</PluginContextContainer>, parent);
}

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task, Progress, Scheduler, MultistepTask, chunkedSubtask } from '../mol-task'
import { Task, Progress, Scheduler, MultistepTask, chunkedSubtask } from '../mol-task';
import { now } from '../mol-util/now';
export async function test1() {
@@ -85,19 +85,19 @@ export const ms = MultistepTask('ms-task', ['step 1', 'step 2', 'step 3'], async
await step(0);
const child = Task.create('chunked', async ctx => {
const s = await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 125 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
const s = await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 125 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p));
return s.i;
});
await child.runAsChild(ctx);
await Scheduler.delay(250);
await step(1);
await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 80 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 80 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p));
await Scheduler.delay(250);
await step(2);
await Scheduler.delay(250);
return p.i + 3;
})
});
export function abortingObserver(p: Progress) {

View File

@@ -4,40 +4,40 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../../mol-theme/theme'
import { ParamDefinition as PD } from '../../../../mol-util/param-definition'
import { Color } from '../../../../mol-util/color'
import { getPalette } from '../../../../mol-util/color/palette'
import { ColorTheme, LocationColor } from '../../../../mol-theme/color'
import { ScaleLegend, TableLegend } from '../../../../mol-util/legend'
import { StructureElement, Bond } from '../../../../mol-model/structure'
import { Location } from '../../../../mol-model/location';
import { CellPackInfoProvider } from './property'
import { distinctColors } from '../../../../mol-util/color/distinct'
import { Hcl } from '../../../../mol-util/color/spaces/hcl'
import { ThemeDataContext } from '../../mol-theme/theme';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Color } from '../../mol-util/color';
import { getPalette } from '../../mol-util/color/palette';
import { ColorTheme, LocationColor } from '../../mol-theme/color';
import { ScaleLegend, TableLegend } from '../../mol-util/legend';
import { StructureElement, Bond } from '../../mol-model/structure';
import { Location } from '../../mol-model/location';
import { CellPackInfoProvider } from './property';
import { distinctColors } from '../../mol-util/color/distinct';
import { Hcl } from '../../mol-util/color/spaces/hcl';
const DefaultColor = Color(0xCCCCCC)
const Description = 'Gives every model in a CellPack packing a unique color similar to other models in the packing.'
const DefaultColor = Color(0xCCCCCC);
const Description = 'Gives every model in a CellPack packing a unique color similar to other models in the packing.';
export const CellPackColorThemeParams = {}
export const CellPackColorThemeParams = {};
export type CellPackColorThemeParams = typeof CellPackColorThemeParams
export function getCellPackColorThemeParams(ctx: ThemeDataContext) {
return CellPackColorThemeParams // TODO return copy
return CellPackColorThemeParams; // TODO return copy
}
export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackColorThemeParams>): ColorTheme<CellPackColorThemeParams> {
let color: LocationColor
let legend: ScaleLegend | TableLegend | undefined
let color: LocationColor;
let legend: ScaleLegend | TableLegend | undefined;
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
if (ctx.structure && info) {
const colors = distinctColors(info.packingsCount)
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex])
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number]
const colors = distinctColors(info.packingsCount);
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
const { models } = ctx.structure.root
const { models } = ctx.structure.root;
let size = 0;
for (const m of models) size = Math.max(size, m.trajectoryInfo.size);
@@ -49,24 +49,24 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75,
minLabel: 'Min', maxLabel: 'Max', valueLabel: (i: number) => `${i + 1}`,
}
}})
legend = palette.legend
const modelColor = new Map<number, Color>()
for (let i = 0, il = models.length; i <il; ++i) {
}});
legend = palette.legend;
const modelColor = new Map<number, Color>();
for (let i = 0, il = models.length; i < il; ++i) {
const idx = models[i].trajectoryInfo.index;
modelColor.set(models[i].trajectoryInfo.index, palette.color(idx))
modelColor.set(models[i].trajectoryInfo.index, palette.color(idx));
}
color = (location: Location): Color => {
if (StructureElement.Location.is(location)) {
return modelColor.get(location.unit.model.trajectoryInfo.index)!
return modelColor.get(location.unit.model.trajectoryInfo.index)!;
} else if (Bond.isLocation(location)) {
return modelColor.get(location.aUnit.model.trajectoryInfo.index)!
return modelColor.get(location.aUnit.model.trajectoryInfo.index)!;
}
return DefaultColor
}
return DefaultColor;
};
} else {
color = () => DefaultColor
color = () => DefaultColor;
}
return {
@@ -76,7 +76,7 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
props,
description: Description,
legend
}
};
}
export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeParams, 'cellpack'> = {
@@ -91,6 +91,6 @@ export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeP
!!ctx.structure && ctx.structure.elementCount > 0 &&
ctx.structure.models[0].trajectoryInfo.size > 1 &&
!!CellPackInfoProvider.get(ctx.structure).value
)
);
}
}
};

View File

@@ -5,8 +5,8 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Quat, Mat4 } from '../../../../mol-math/linear-algebra';
import { NumberArray } from '../../../../mol-util/type-helpers';
import { Vec3, Quat, Mat4 } from '../../mol-math/linear-algebra';
import { NumberArray } from '../../mol-util/type-helpers';
interface Frame {
t: Vec3,
@@ -14,215 +14,210 @@ interface Frame {
s: Vec3,
}
const a0Tmp = Vec3()
const a1Tmp = Vec3()
const a2Tmp = Vec3()
const a3Tmp = Vec3()
const a0Tmp = Vec3();
const a1Tmp = Vec3();
const a2Tmp = Vec3();
const a3Tmp = Vec3();
function CubicInterpolate(out: Vec3, y0: Vec3, y1: Vec3, y2: Vec3, y3: Vec3, mu: number): Vec3 {
const mu2 = mu * mu;
Vec3.sub(a0Tmp, y3, y2)
Vec3.sub(a0Tmp, a0Tmp, y0)
Vec3.add(a0Tmp, a0Tmp, y1)
Vec3.sub(a0Tmp, y3, y2);
Vec3.sub(a0Tmp, a0Tmp, y0);
Vec3.add(a0Tmp, a0Tmp, y1);
Vec3.sub(a1Tmp, y0, y1)
Vec3.sub(a1Tmp, a1Tmp, a0Tmp)
Vec3.sub(a1Tmp, y0, y1);
Vec3.sub(a1Tmp, a1Tmp, a0Tmp);
Vec3.sub(a2Tmp, y2, y0)
Vec3.sub(a2Tmp, y2, y0);
Vec3.copy(a3Tmp, y1)
Vec3.copy(a3Tmp, y1);
out[0] = a0Tmp[0] * mu * mu2 + a1Tmp[0] * mu2 + a2Tmp[0] * mu + a3Tmp[0]
out[1] = a0Tmp[1] * mu * mu2 + a1Tmp[1] * mu2 + a2Tmp[1] * mu + a3Tmp[1]
out[2] = a0Tmp[2] * mu * mu2 + a1Tmp[2] * mu2 + a2Tmp[2] * mu + a3Tmp[2]
out[0] = a0Tmp[0] * mu * mu2 + a1Tmp[0] * mu2 + a2Tmp[0] * mu + a3Tmp[0];
out[1] = a0Tmp[1] * mu * mu2 + a1Tmp[1] * mu2 + a2Tmp[1] * mu + a3Tmp[1];
out[2] = a0Tmp[2] * mu * mu2 + a1Tmp[2] * mu2 + a2Tmp[2] * mu + a3Tmp[2];
return out
return out;
}
const cp0 = Vec3()
const cp1 = Vec3()
const cp2 = Vec3()
const cp3 = Vec3()
const currentPosition = Vec3()
const cp0 = Vec3();
const cp1 = Vec3();
const cp2 = Vec3();
const cp3 = Vec3();
const currentPosition = Vec3();
function ResampleControlPoints(points: NumberArray, segmentLength: number) {
const nP = points.length / 3
const nP = points.length / 3;
// insert a point at the end and at the begining
// controlPoints.Insert(0, controlPoints[0] + (controlPoints[0] - controlPoints[1]) / 2.0f);
// controlPoints.Add(controlPoints[nP - 1] + (controlPoints[nP - 1] - controlPoints[nP - 2]) / 2.0f);
let resampledControlPoints: Vec3[] = []
let resampledControlPoints: Vec3[] = [];
// resampledControlPoints.Add(controlPoints[0]);
// resampledControlPoints.Add(controlPoints[1]);
let idx = 1
let idx = 1;
// const currentPosition = Vec3.create(points[idx * 3], points[idx * 3 + 1], points[idx * 3 + 2])
Vec3.fromArray(currentPosition, points, idx * 3)
Vec3.fromArray(currentPosition, points, idx * 3);
let lerpValue = 0.0
let lerpValue = 0.0;
// Normalize the distance between control points
while (true) {
if (idx + 2 >= nP) break
Vec3.fromArray(cp0, points, (idx - 1) * 3)
Vec3.fromArray(cp1, points, idx * 3)
Vec3.fromArray(cp2, points, (idx + 1) * 3)
Vec3.fromArray(cp3, points, (idx + 2) * 3)
if (idx + 2 >= nP) break;
Vec3.fromArray(cp0, points, (idx - 1) * 3);
Vec3.fromArray(cp1, points, idx * 3);
Vec3.fromArray(cp2, points, (idx + 1) * 3);
Vec3.fromArray(cp3, points, (idx + 2) * 3);
// const cp0 = Vec3.create(points[(idx-1)*3], points[(idx-1)*3+1], points[(idx-1)*3+2]) // controlPoints[currentPointId - 1];
// const cp1 = Vec3.create(points[idx*3], points[idx*3+1], points[idx*3+2]) // controlPoints[currentPointId];
// const cp2 = Vec3.create(points[(idx+1)*3], points[(idx+1)*3+1], points[(idx+1)*3+2]) // controlPoints[currentPointId + 1];
// const cp3 = Vec3.create(points[(idx+2)*3], points[(idx+2)*3+1], points[(idx+2)*3+2]); // controlPoints[currentPointId + 2];
let found = false
let found = false;
for (; lerpValue <= 1; lerpValue += 0.01) {
// lerp?slerp
// let candidate:Vec3 = Vec3.lerp(Vec3.zero(), cp0, cp1, lerpValue);
// const candidate:Vec3 = Vec3.bezier(Vec3.zero(), cp0, cp1, cp2, cp3, lerpValue);
const candidate = CubicInterpolate(Vec3(), cp0, cp1, cp2, cp3, lerpValue)
const candidate = CubicInterpolate(Vec3(), cp0, cp1, cp2, cp3, lerpValue);
const d = Vec3.distance(currentPosition, candidate);
if (d > segmentLength) {
resampledControlPoints.push(candidate)
Vec3.copy(currentPosition, candidate)
found = true
break
resampledControlPoints.push(candidate);
Vec3.copy(currentPosition, candidate);
found = true;
break;
}
}
if (!found) {
lerpValue = 0
idx += 1
lerpValue = 0;
idx += 1;
}
}
return resampledControlPoints
return resampledControlPoints;
}
const prevV = Vec3()
const tmpV1 = Vec3()
const tmpV2 = Vec3()
const tmpV3 = Vec3()
const prevV = Vec3();
const tmpV1 = Vec3();
const tmpV2 = Vec3();
const tmpV3 = Vec3();
// easier to align to theses normals
function GetSmoothNormals(points: Vec3[]) {
const nP: number = points.length;
const smoothNormals: Vec3[] = []
const smoothNormals: Vec3[] = [];
if (points.length < 3) {
for (let i = 0; i < points.length; ++i)
smoothNormals.push(Vec3.normalize(Vec3(), points[i]))
smoothNormals.push(Vec3.normalize(Vec3(), points[i]));
return smoothNormals;
}
let p0 = points[0]
let p1 = points[1]
let p2 = points[2]
const p21 = Vec3.sub(tmpV1, p2, p1)
const p01 = Vec3.sub(tmpV2, p0, p1)
const p0121 = Vec3.cross(tmpV3, p01, p21)
Vec3.normalize(prevV, p0121)
smoothNormals.push(Vec3.clone(prevV))
let p0 = points[0];
let p1 = points[1];
let p2 = points[2];
const p21 = Vec3.sub(tmpV1, p2, p1);
const p01 = Vec3.sub(tmpV2, p0, p1);
const p0121 = Vec3.cross(tmpV3, p01, p21);
Vec3.normalize(prevV, p0121);
smoothNormals.push(Vec3.clone(prevV));
for (let i = 1; i < points.length - 1; ++i) {
p0 = points[i - 1]
p1 = points[i]
p2 = points[i + 1]
const t = Vec3.normalize(tmpV1, Vec3.sub(tmpV1, p2 , p0))
const b = Vec3.normalize(tmpV2, Vec3.cross(tmpV2, t, prevV))
const n = Vec3.normalize(Vec3(), Vec3.cross(tmpV3, t, b))
Vec3.negate(n, n)
Vec3.copy(prevV, n)
smoothNormals.push(n)
p0 = points[i - 1];
p1 = points[i];
p2 = points[i + 1];
const t = Vec3.normalize(tmpV1, Vec3.sub(tmpV1, p2, p0));
const b = Vec3.normalize(tmpV2, Vec3.cross(tmpV2, t, prevV));
const n = Vec3.normalize(Vec3(), Vec3.cross(tmpV3, t, b));
Vec3.negate(n, n);
Vec3.copy(prevV, n);
smoothNormals.push(n);
}
const last = Vec3()
const last = Vec3();
Vec3.normalize(last, Vec3.cross(last,
Vec3.sub(tmpV1, points[nP - 3], points[nP-2]),
Vec3.sub(tmpV2, points[nP-2] , points[nP-1]))
)
smoothNormals.push(last)
Vec3.sub(tmpV1, points[nP - 3], points[nP - 2]),
Vec3.sub(tmpV2, points[nP - 2], points[nP - 1]))
);
smoothNormals.push(last);
return smoothNormals;
}
const frameTmpV1 = Vec3()
const frameTmpV2 = Vec3()
const frameTmpV3 = Vec3()
const frameTmpV1 = Vec3();
const frameTmpV2 = Vec3();
const frameTmpV3 = Vec3();
function getFrame(reference: Vec3, tangent: Vec3) {
const t = Vec3.normalize(Vec3(), tangent);
// make reference vector orthogonal to tangent
const proj_r_to_t = Vec3.scale(
frameTmpV1, tangent, Vec3.dot(reference, tangent) / Vec3.dot(tangent, tangent)
)
const r = Vec3.normalize(Vec3(), Vec3.sub(frameTmpV2, reference, proj_r_to_t))
);
const r = Vec3.normalize(Vec3(), Vec3.sub(frameTmpV2, reference, proj_r_to_t));
// make bitangent vector orthogonal to the others
const s = Vec3.normalize(Vec3(), Vec3.cross(frameTmpV3, t, r))
return { t, r, s }
const s = Vec3.normalize(Vec3(), Vec3.cross(frameTmpV3, t, r));
return { t, r, s };
}
const mfTmpV1 = Vec3()
const mfTmpV2 = Vec3()
const mfTmpV3 = Vec3()
const mfTmpV4 = Vec3()
const mfTmpV5 = Vec3()
const mfTmpV6 = Vec3()
const mfTmpV7 = Vec3()
const mfTmpV8 = Vec3()
const mfTmpV9 = Vec3()
const mfTmpV1 = Vec3();
const mfTmpV2 = Vec3();
const mfTmpV3 = Vec3();
const mfTmpV4 = Vec3();
const mfTmpV5 = Vec3();
const mfTmpV6 = Vec3();
const mfTmpV7 = Vec3();
const mfTmpV8 = Vec3();
const mfTmpV9 = Vec3();
// easier to align to theses normals
// https://github.com/bzamecnik/gpg/blob/master/rotation-minimizing-frame/rmf.py
function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
const frames: Frame[] = [];
const t0 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[1], points[0]))
frames.push(getFrame(normals[0], t0))
const t0 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[1], points[0]));
frames.push(getFrame(normals[0], t0));
for (let i = 0; i< points.length-2; ++i) {
const t2 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[i+2], points[i+1]))
const v1 = Vec3.sub(mfTmpV2, points[i + 1], points[i]) // this is tangeant
const c1 = Vec3.dot(v1, v1)
for (let i = 0; i < points.length - 2; ++i) {
const t2 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[i + 2], points[i + 1]));
const v1 = Vec3.sub(mfTmpV2, points[i + 1], points[i]); // this is tangeant
const c1 = Vec3.dot(v1, v1);
// compute r_i^L = R_1 * r_i
const v1r = Vec3.scale(mfTmpV3, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].r))
const ref_L_i = Vec3.sub(mfTmpV4, frames[i].r, v1r)
const v1r = Vec3.scale(mfTmpV3, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].r));
const ref_L_i = Vec3.sub(mfTmpV4, frames[i].r, v1r);
// compute t_i^L = R_1 * t_i
const v1t = Vec3.scale(mfTmpV5, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].t))
const tan_L_i = Vec3.sub(mfTmpV6, frames[i].t, v1t)
const v1t = Vec3.scale(mfTmpV5, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].t));
const tan_L_i = Vec3.sub(mfTmpV6, frames[i].t, v1t);
// # compute reflection vector of R_2
const v2 = Vec3.sub(mfTmpV7, t2 , tan_L_i)
const c2 = Vec3.dot(v2, v2)
const v2 = Vec3.sub(mfTmpV7, t2, tan_L_i);
const c2 = Vec3.dot(v2, v2);
// compute r_(i+1) = R_2 * r_i^L
const v2l = Vec3.scale(mfTmpV8, v1, (2.0/c2) * Vec3.dot(v2, ref_L_i))
const ref_next = Vec3.sub(mfTmpV9, ref_L_i, v2l) // ref_L_i - (2 / c2) * v2.dot(ref_L_i) * v2
frames.push(getFrame(ref_next, t2)) // frames.append(Frame(ref_next, tangents[i+1]))
const v2l = Vec3.scale(mfTmpV8, v1, (2.0 / c2) * Vec3.dot(v2, ref_L_i));
const ref_next = Vec3.sub(mfTmpV9, ref_L_i, v2l); // ref_L_i - (2 / c2) * v2.dot(ref_L_i) * v2
frames.push(getFrame(ref_next, t2)); // frames.append(Frame(ref_next, tangents[i+1]))
}
return frames;
}
const rpTmpVec1 = Vec3()
const rpTmpVec1 = Vec3();
export function getMatFromResamplePoints(points: NumberArray) {
const segmentLength = 3.4
const new_points = ResampleControlPoints(points, 3.4)
const npoints = new_points.length
const new_normal = GetSmoothNormals(new_points)
const frames = GetMiniFrame(new_points, new_normal)
const limit = npoints
const transforms: Mat4[] = []
export function getMatFromResamplePoints(points: NumberArray, segmentLength: number) {
const new_points = ResampleControlPoints(points, segmentLength);
const npoints = new_points.length;
const new_normal = GetSmoothNormals(new_points);
const frames = GetMiniFrame(new_points, new_normal);
const limit = npoints;
const transforms: Mat4[] = [];
const pti = Vec3.copy(rpTmpVec1, new_points[0]);
// console.log(new_points.length)
// console.log(points.length/3)
// console.log(limit)
// console.log(segmentLength)
for (let i = 0; i<npoints-2; ++i) {
const pti1: Vec3 = new_points[i+1] // Vec3.create(points[(i+1)*3],points[(i+1)*3+1],points[(i+1)*3+2]);
const d = Vec3.distance(pti, pti1)
for (let i = 0; i < npoints - 2; ++i) {
const pti1: Vec3 = new_points[i + 1]; // Vec3.create(points[(i+1)*3],points[(i+1)*3+1],points[(i+1)*3+2]);
const d = Vec3.distance(pti, pti1);
if (d >= segmentLength) {
// use twist or random?
const quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t) // Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),new_normal[i]);//Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),direction);new_normal
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random()*3.60 ) // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
const m = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat)) // Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),quat1,quat2));//Mat4.fromQuat(Mat4.zero(),quat);//Mat4.identity();//Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),rq,quat));
const quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t); // Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),new_normal[i]);//Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),direction);new_normal
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random() * 3.60 ); // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
const m = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat)); // Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),quat1,quat2));//Mat4.fromQuat(Mat4.zero(),quat);//Mat4.identity();//Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),rq,quat));
// let pos:Vec3 = Vec3.add(Vec3.zero(),pti1,pti)
// pos = Vec3.scale(pos,pos,1.0/2.0);
// Vec3.makeRotation(Mat4.zero(),Vec3.create(0,0,1),frames[i].t);//
Mat4.setTranslation(m, pti1)
Mat4.setTranslation(m, pti1);
// let m2:Mat4 = GetTubePropertiesMatrix(pti,pti1);
// let q:Quat = Quat.rotationTo(Quat.zero(), Vec3.create(0,1,0),Vec3.create(0,0,1))
// m2=Mat4.mul(Mat4.identity(),Mat4.fromQuat(Mat4.zero(),q),m2);
transforms.push(m)
Vec3.copy(pti, pti1)
transforms.push(m);
Vec3.copy(pti, pti1);
}
if (transforms.length >= limit) break
if (transforms.length >= limit) break;
}
return transforms
return transforms;
}

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Quat } from '../../../../mol-math/linear-algebra';
import { Vec3, Quat } from '../../mol-math/linear-algebra';
export interface CellPack {
cell: Cell
@@ -27,8 +27,7 @@ export interface Cell {
export interface Recipe {
setupfile: string
/** First entry is name, secound is path: [name: string, path: string][] */
paths: [string, string][]
paths: [string, string][] // [name: string, path: string][]
version: string
name: string
}
@@ -42,21 +41,41 @@ export interface Packing {
ingredients: { [key: string]: Ingredient }
}
export interface Ingredient {
source: IngredientSource
results: [Vec3, Quat][]
name: string
positions?: [Vec3[]] // why wrapped in an extra array?
radii?: [number[]] // why wrapped in an extra array?
export interface Positions {
coords?: Vec3[];
}
export interface Radii {
radii?: number[];
}
export interface Ingredient {
source: IngredientSource;
results: [Vec3, Quat][];
name: string;
/** Vec3[]];CoarseGraind Beads coordinates LOD */
positions?: [Positions];
/** number[]];CoarseGraind Beads radii LOD */
radii?: [Radii];
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
nbCurve?: number
nbCurve?: number;
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
[curveX: string]: unknown
[curveX: string]: unknown;
/** the orientation in the membrane */
principalAxis?: Vec3;
/** offset along membrane */
offset?: Vec3;
ingtype?: string;
}
export interface IngredientSource {
pdb: string
transform: { center: boolean, translate?: Vec3 }
biomt?: boolean
}
pdb: string;
bu?: string; /** biological unit e.g AU,BU1,etc.. */
selection?: string; /** NGL selection or :A or :B etc.. */
model?: string; /** model number e.g 0,1,2... */
transform: {
center: boolean;
translate?: Vec3;
};
biomt?: boolean;
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginBehavior } from '../../mol-plugin/behavior';
import { CellPackColorThemeProvider } from './color';
import { LoadCellPackModel } from './model';
export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'cellpack',
category: 'custom-props',
display: {
name: 'CellPack',
description: 'CellPack Model Loading and Viewing.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
register(): void {
this.ctx.state.data.actions.add(LoadCellPackModel);
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackColorThemeProvider);
}
unregister() {
this.ctx.state.data.actions.remove(LoadCellPackModel);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackColorThemeProvider);
}
}
});

View File

@@ -0,0 +1,524 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateAction, StateBuilder, StateTransformer, State } from '../../mol-state';
import { PluginContext } from '../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Ingredient, IngredientSource, CellPacking } from './data';
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../mol-math/geometry';
import { Task, RuntimeContext } from '../../mol-task';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../mol-script/runtime/query/compiler';
import { CifCategory, CifField } from '../../mol-io/reader/cif';
import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
import { Column } from '../../mol-data/db';
import { createModels } from '../../mol-model-formats/structure/basic/parser';
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
import { Asset } from '../../mol-util/assets';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
}
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, file?: Asset.File) {
const assetManager = plugin.managers.asset;
const model_id = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
let model: Model;
let assets: Asset.Wrapper[] = [];
if (file) {
if (file.name.endsWith('.cif')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const cif = (await parseCif(plugin, text.data)).blocks[0];
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
} else if (file.name.endsWith('.bcif')) {
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
assets.push(binary);
const cif = (await parseCif(plugin, binary.data)).blocks[0];
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
} else if (file.name.endsWith('.pdb')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const pdb = await parsePDBfile(plugin, text.data, id);
model = (await plugin.runTask(trajectoryFromPDB(pdb)))[model_id];
} else {
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
const data = await getFromOPM(plugin, id, assetManager);
if (data.asset){
assets.push(data.asset);
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
}
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
}
} else {
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
assets.push(data.asset);
if ('pdb' in data) {
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
} else {
model = (await plugin.runTask(trajectoryFromMmCIF(data.mmcif)))[model_id];
}
}
return { model, assets };
}
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model);
const { assembly } = props;
if (assembly) {
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
}
let query;
if (source.selection){
const asymIds: string[] = source.selection.replace(' :', '').split(' or');
query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('auth_asym_id')])
})
]);
} else {
query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
})
]);
}
const compiled = compile<StructureSelection>(query);
const result = compiled(new QueryContext(structure));
structure = StructureSelection.unionStructure(result);
return structure;
}
function getTransformLegacy(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2]);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
Mat4.transpose(m, m);
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0));
Mat4.setTranslation(m, trans);
return m;
}
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3]);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
const p: Vec3 = Vec3.create(trans[0], trans[1], trans[2]);
Mat4.setTranslation(m, p);
return m;
}
function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]));
else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]));
}
function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0;
const instances: Mat4[] = [];
const segmentLength = ingredient.radii
? (ingredient.radii[0].radii
? ingredient.radii[0].radii[0] * 2.0
: 3.4)
: 3.4;
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`;
if (!(cname in ingredient)) {
// console.warn(`Expected '${cname}' in ingredient`)
continue;
}
const _points = ingredient[cname] as Vec3[];
if (_points.length <= 2) {
// TODO handle curve with 2 or less points
continue;
}
const points = new Float32Array(_points.length * 3);
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3);
const newInstances = getMatFromResamplePoints(points, segmentLength);
instances.push(...newInstances);
}
return instances;
}
function getAssembly(transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder();
const { units } = structure;
for (let i = 0, il = transforms.length; i < il; ++i) {
const id = `${i + 1}`;
const op = SymmetryOperator.create(id, transforms[i], { assembly: { id, operId: i, operList: [ id ] } });
for (const unit of units) {
builder.addWithOperator(unit, op);
}
}
return builder.getStructure();
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed');
const { db } = model.sourceData.data;
const d = db.atom_site;
const n = d._rowCount;
const rowCount = n * transforms.length;
const { offsets, count } = model.atomicHierarchy.chainAtomSegments;
const x = d.Cartn_x.toArray();
const y = d.Cartn_y.toArray();
const z = d.Cartn_z.toArray();
const Cartn_x = new Float32Array(rowCount);
const Cartn_y = new Float32Array(rowCount);
const Cartn_z = new Float32Array(rowCount);
const map = new Uint32Array(rowCount);
const seq = new Int32Array(rowCount);
let offset = 0;
for (let c = 0; c < count; ++c) {
const cStart = offsets[c];
const cEnd = offsets[c + 1];
const cLength = cEnd - cStart;
for (let t = 0, tl = transforms.length; t < tl; ++t) {
const m = transforms[t];
for (let j = cStart; j < cEnd; ++j) {
const i = offset + j - cStart;
const xj = x[j], yj = y[j], zj = z[j];
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12];
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13];
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14];
map[i] = j;
seq[i] = t + 1;
}
offset += cLength;
}
}
function multColumn<T>(column: Column<T>) {
const array = column.toArray();
return Column.ofLambda({
value: row => array[map[row]],
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
rowCount, schema: column.schema
});
}
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
auth_seq_id: CifField.ofNumbers(seq),
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
Cartn_x: CifField.ofNumbers(Cartn_x),
Cartn_y: CifField.ofNumbers(Cartn_y),
Cartn_z: CifField.ofNumbers(Cartn_z),
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
id: CifField.ofColumn(Column.ofLambda({
value: row => row,
areValuesEqual: (rowA, rowB) => rowA === rowB,
rowCount, schema: d.id.schema,
})),
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
label_seq_id: CifField.ofNumbers(seq),
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
};
const categories = {
entity: CifCategory.ofTable('entity', db.entity),
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
};
return {
header: name,
categoryNames: Object.keys(categories),
categories
};
}
async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model);
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = MmcifFormat.fromFrame(cif);
const models = await createModels(format.data.db, format, ctx);
return models[0];
});
const curveModel = await plugin.runTask(curveModelTask);
return getStructure(plugin, curveModel, ingredient.source);
}
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
const { name, source, results, nbCurve } = ingredient;
if (source.pdb === 'None') return;
const file = ingredientFiles[source.pdb];
if (!file) {
// TODO can these be added to the library?
if (name === 'HIV1_CAhex_0_1_0') return;
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return;
if (name === 'iLDL') return;
if (name === 'peptides') return;
if (name === 'lypoglycane') return;
}
// model id in case structure is NMR
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, file);
if (!model) return;
let structure: Structure;
if (nbCurve) {
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
} else {
let bu: string|undefined = source.bu ? source.bu : undefined;
if (bu){
if (bu === 'AU') {
bu = undefined;
} else {
bu = bu.slice(2);
}
}
structure = await getStructure(plugin, model, source, { assembly: bu });
// transform with offset and pcp
let legacy: boolean = true;
if (ingredient.offset || ingredient.principalAxis){
legacy = false;
const structureMean = getStructureMean(structure);
Vec3.negate(structureMean, structureMean);
const m1: Mat4 = Mat4.identity();
Mat4.setTranslation(m1, structureMean);
structure = Structure.transform(structure, m1);
if (ingredient.offset){
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())){
const m: Mat4 = Mat4.identity();
Mat4.setTranslation(m, ingredient.offset);
structure = Structure.transform(structure, m);
}
}
if (ingredient.principalAxis){
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
const q: Quat = Quat.identity();
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
structure = Structure.transform(structure, m);
}
}
}
structure = getAssembly(getResultTransforms(results, legacy), structure);
}
return { structure, assets };
}
export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing;
const assets: Asset.Wrapper[] = [];
const structures: Structure[] = [];
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles);
if (ingredientStructure) {
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
}
}
if (ctx.shouldUpdate) await ctx.update(`${name} - units`);
const builder = Structure.Builder({ label: name });
let offsetInvariantId = 0;
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`);
let maxInvariantId = 0;
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId;
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
}
offsetInvariantId += maxInvariantId + 1;
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
const structure = builder.getStructure();
for( let i = 0, il = structure.models.length; i < il; ++i) {
const { trajectoryInfo } = structure.models[i];
trajectoryInfo.size = il;
trajectoryInfo.index = i;
}
return { structure, assets };
});
}
async function handleHivRna(plugin: PluginContext, packings: CellPacking[], baseUrl: string) {
for (let i = 0, il = packings.length; i < il; ++i) {
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
const url = Asset.getUrlAsset(plugin.managers.asset, `${baseUrl}/extras/rna_allpoints.json`);
const json = await plugin.runTask(plugin.managers.asset.resolve(url, 'json', false));
const points = json.data.points as number[];
const curve0: Vec3[] = [];
for (let j = 0, jl = points.length; j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j));
}
packings[i].ingredients['RNA'] = {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
};
}
}
}
async function loadMembrane(plugin: PluginContext, name: string, state: State, params: LoadCellPackModelParams) {
let file: Asset.File | undefined = undefined;
if (params.ingredients.files !== null) {
const fileName = `${name}.bcif`;
for (const f of params.ingredients.files) {
if (fileName === f.name) {
file = f;
break;
}
}
}
let b = state.build().toRoot();
if (file) {
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
} else {
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
}
const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.StructureFromModel)
.commit({ revertOnError: true });
const membraneParams = {
representation: params.preset.representation,
};
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
}
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
if (params.source.name === 'id') {
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
} else {
const file = params.source.params;
if (file === null) {
plugin.log.error('No file selected');
return;
}
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
}
const cellPackBuilder = cellPackJson
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack);
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
const { packings } = cellPackObject.obj!.data;
await handleHivRna(plugin, packings, params.baseUrl);
for (let i = 0, il = packings.length; i < il; ++i) {
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files };
const packing = await state.build()
.to(cellPackBuilder.ref)
.apply(StructureFromCellpack, p)
.commit({ revertOnError: true });
const packingParams = {
traceOnly: params.preset.traceOnly,
representation: params.preset.representation,
};
await CellpackPackingPreset.apply(packing, packingParams, plugin);
if ( packings[i].location === 'surface' ){
await loadMembrane(plugin, packings[i].name, state, params);
}
}
}
const LoadCellPackModelParams = {
source: PD.MappedStatic('id', {
'id': PD.Select('influenza_model1.json', [
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
['hiv_lipids.bcif', 'hiv_lipids'],
['influenza_model1.json', 'influenza_model1'],
['ExosomeModel.json', 'ExosomeModel'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
] as const),
'file': PD.File({ accept: 'id' }),
}, { options: [['id', 'Id'], ['file', 'File']] }),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
ingredients : PD.Group({
files: PD.FileList({ accept: '.cif,.bcif,.pdb' })
}, { isExpanded: true }),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation']))
}, { isExpanded: true })
};
type LoadCellPackModelParams = PD.Values<typeof LoadCellPackModelParams>
export const LoadCellPackModel = StateAction.build({
display: { name: 'Load CellPack', description: 'Open or download a model' },
params: LoadCellPackModelParams,
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
if (params.source.name === 'id' && params.source.params === 'hiv_lipids.bcif') {
await loadMembrane(ctx, 'hiv_lipids', state, params);
} else {
await loadPackings(ctx, taskCtx, state, params);
}
}));

View File

@@ -4,16 +4,16 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateObjectRef } from '../../../../mol-state';
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../../../mol-plugin-state/builder/structure/representation-preset';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { ColorNames } from '../../../../mol-util/color/names';
import { StateObjectRef } from '../../mol-state';
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ColorNames } from '../../mol-util/color/names';
import { CellPackColorThemeProvider } from './color';
export const CellpackPackingPresetParams = {
traceOnly: PD.Boolean(true),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
}
};
export type CellpackPackingPresetParams = PD.ValuesFor<typeof CellpackPackingPresetParams>
export const CellpackPackingPreset = StructureRepresentationPresetProvider({
@@ -35,18 +35,18 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
if (params.representation === 'gaussian-surface') {
Object.assign(reprProps, {
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
})
});
} else if (params.representation === 'spacefill' && params.traceOnly) {
Object.assign(reprProps, { sizeFactor: 2 })
Object.assign(reprProps, { sizeFactor: 2 });
}
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
const color = CellPackColorThemeProvider.name
const color = CellPackColorThemeProvider.name;
const representations = {
polymer: builder.buildRepresentation<any>(update, components.polymer, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color }, { tag: 'polymer' })
};
await plugin.updateDataState(update, { revertOnError: true });
await update.commit({ revertOnError: true });
return { components, representations };
}
});
@@ -55,7 +55,7 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
export const CellpackMembranePresetParams = {
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
}
};
export type CellpackMembranePresetParams = PD.ValuesFor<typeof CellpackMembranePresetParams>
export const CellpackMembranePreset = StructureRepresentationPresetProvider({
@@ -76,7 +76,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
if (params.representation === 'gaussian-surface') {
Object.assign(reprProps, {
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
})
});
}
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
@@ -84,7 +84,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
membrane: builder.buildRepresentation(update, components.membrane, { type: 'gaussian-surface', typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
};
await plugin.updateDataState(update, { revertOnError: true });
await update.commit({ revertOnError: true });
return { components, representations };
}
});

View File

@@ -4,10 +4,10 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomStructureProperty } from '../../../../mol-model-props/common/custom-structure-property'
import { Structure, CustomPropertyDescriptor } from '../../../../mol-model/structure'
import { CustomProperty } from '../../../../mol-model-props/common/custom-property'
import { ParamDefinition as PD } from '../../../../mol-util/param-definition'
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
import { CustomProperty } from '../../mol-model-props/common/custom-property';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
export type CellPackInfoValue = {
packingsCount: number
@@ -16,7 +16,7 @@ export type CellPackInfoValue = {
const CellPackInfoParams = {
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0 }, { isHidden: true })
}
};
type CellPackInfoParams = PD.Values<typeof CellPackInfoParams>
export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellPackInfoParams, CellPackInfoValue> = CustomStructureProperty.createProvider({
@@ -27,6 +27,8 @@ export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellP
getParams: (data: Structure) => CellPackInfoParams,
isApplicable: (data: Structure) => true,
obtain: async (ctx: CustomProperty.Context, data: Structure, props: CellPackInfoParams) => {
return { ...CellPackInfoParams.info.defaultValue, ...props.info }
return {
value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
};
}
})
});

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Task } from '../../mol-task';
import { CellPack as _CellPack, Cell, CellPacking } from './data';
import { createStructureFromCellPack } from './model';
import { IngredientFiles } from './util';
import { Asset } from '../../mol-util/assets';
import { PluginContext } from '../../mol-plugin/context';
import { CellPackInfoProvider } from './property';
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
export { ParseCellPack };
type ParseCellPack = typeof ParseCellPack
const ParseCellPack = PluginStateTransform.BuiltIn({
name: 'parse-cellpack',
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
from: PSO.Format.Json,
to: CellPack
})({
apply({ a }) {
return Task.create('Parse CellPack', async ctx => {
const cell = a.data as Cell;
const packings: CellPacking[] = [];
const { compartments, cytoplasme } = cell;
if (compartments) {
for (const name in compartments) {
const { surface, interior } = compartments[name];
if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients });
if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients });
}
}
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
return new CellPack({ cell, packings });
});
}
});
export { StructureFromCellpack };
type StructureFromCellpack = typeof ParseCellPack
const StructureFromCellpack = PluginStateTransform.BuiltIn({
name: 'structure-from-cellpack',
display: { name: 'Structure from CellPack', description: 'Create Structure from CellPack Packing' },
from: CellPack,
to: PSO.Molecule.Structure,
params: a => {
const options = a ? a.data.packings.map((d, i) => [i, d.name] as const) : [];
return {
packing: PD.Select(0, options),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
ingredientFiles: PD.FileList({ accept: '.cif,.bcif,.pdb' })
};
}
})({
apply({ a, params, cache }, plugin: PluginContext) {
return Task.create('Structure from CellPack', async ctx => {
const packing = a.data.packings[params.packing];
const ingredientFiles: IngredientFiles = {};
if (params.ingredientFiles !== null) {
for (const file of params.ingredientFiles) {
ingredientFiles[file.name] = file;
}
}
const { structure, assets } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
info: { packingsCount: a.data.packings.length, packingIndex: params.packing }
});
(cache as any).assets = assets;
return new PSO.Molecule.Structure(structure, { label: packing.name });
});
},
dispose({ b, cache }) {
const assets = (cache as any).assets as Asset.Wrapper[];
if(assets) {
for (const a of assets) a.dispose();
}
if (b) {
b.data.customPropertyDescriptors.dispose();
for (const m of b.data.models) {
m.customProperties.dispose();
}
}
}
});

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CIF } from '../../mol-io/reader/cif';
import { parsePDB } from '../../mol-io/reader/pdb/parser';
import { AssetManager, Asset } from '../../mol-util/assets';
import { Structure } from '../../mol-model/structure';
import { Vec3 } from '../../mol-math/linear-algebra';
import { PluginContext } from '../../mol-plugin/context';
export async function parseCif(plugin: PluginContext, data: string | Uint8Array) {
const comp = CIF.parse(data);
const parsed = await plugin.runTask(comp);
if (parsed.isError) throw parsed;
return parsed.result;
}
export async function parsePDBfile(plugin: PluginContext, data: string, id: string) {
const comp = parsePDB(data, id);
const parsed = await plugin.runTask(comp);
if (parsed.isError) throw parsed;
return parsed.result;
}
async function downloadCif(plugin: PluginContext, url: string, isBinary: boolean, assetManager: AssetManager) {
const type = isBinary ? 'binary' : 'string';
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, url), type));
return { cif: await parseCif(plugin, asset.data), asset };
}
async function downloadPDB(plugin: PluginContext, url: string, id: string, assetManager: AssetManager) {
const asset = await assetManager.resolve(Asset.getUrlAsset(assetManager, url), 'string').run();
return { pdb: await parsePDBfile(plugin, asset.data, id), asset };
}
export async function getFromPdb(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
return { mmcif: cif.blocks[0], asset };
}
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager){
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, `https://opm-assets.storage.googleapis.com/pdb/${pdbId.toLowerCase()}.pdb`), 'string'));
return { pdb: await parsePDBfile(plugin, asset.data, pdbId), asset };
}
export async function getFromCellPackDB(plugin: PluginContext, id: string, baseUrl: string, assetManager: AssetManager) {
if (id.toLowerCase().endsWith('.cif') || id.toLowerCase().endsWith('.bcif')) {
const isBinary = id.toLowerCase().endsWith('.bcif');
const { cif, asset } = await downloadCif(plugin, `${baseUrl}/other/${id}`, isBinary, assetManager);
return { mmcif: cif.blocks[0], asset };
} else {
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id;
return await downloadPDB(plugin, `${baseUrl}/other/${name}.pdb`, name, assetManager);
}
}
export type IngredientFiles = { [name: string]: Asset.File }
export function getStructureMean(structure: Structure) {
let xSum = 0, ySum = 0, zSum = 0;
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i];
const { elements } = unit;
const { x, y, z } = unit.conformation;
for (let j = 0, jl = elements.length; j < jl; ++j) {
const eI = elements[j];
xSum += x(eI);
ySum += y(eI);
zSum += z(eI);
}
}
const { elementCount } = structure;
return Vec3.create(xSum / elementCount, ySum / elementCount, zSum / elementCount);
}

View File

@@ -0,0 +1,9 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export { PDBeStructureQualityReport } from './structure-quality-report/behavior';
export { PDBePreferredAssembly } from './preferred-assembly';
export { PDBeStructRefDomain } from './struct-ref-domain';

View File

@@ -15,7 +15,7 @@ export namespace PDBePreferredAssembly {
export type Property = string
export function getFirstFromModel(model: Model): Property {
const symmetry = ModelSymmetry.Provider.get(model)
const symmetry = ModelSymmetry.Provider.get(model);
return symmetry?.assemblies.length ? symmetry.assemblies[0].id : '';
}

View File

@@ -8,7 +8,7 @@ import { Column, Table } from '../../mol-data/db';
import { toTable } from '../../mol-io/reader/cif/schema';
import { CifWriter } from '../../mol-io/writer/cif';
import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
import { PropertyWrapper } from '../common/wrapper';
import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
export namespace PDBeStructRefDomain {
@@ -111,7 +111,7 @@ function fromPDBeJson(modelData: Model, data: any): PDBeStructRefDomain.Property
beg_pdbx_PDB_ins_code: map.start.author_insertion_code,
end_label_seq_id: map.end.residue_number,
end_pdbx_PDB_ins_code: map.end.author_insertion_code,
})
});
}
}
}

View File

@@ -4,13 +4,13 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { OrderedSet } from '../../../../../mol-data/int';
import { StructureQualityReport, StructureQualityReportProvider } from '../../../../../mol-model-props/pdbe/structure-quality-report';
import { StructureQualityReportColorThemeProvider } from '../../../../../mol-model-props/pdbe/themes/structure-quality-report';
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';
import { OrderedSet } from '../../../mol-data/int';
import { StructureQualityReport, StructureQualityReportProvider } from './prop';
import { StructureQualityReportColorThemeProvider } from './color';
import { Loci } from '../../../mol-model/loci';
import { StructureElement } from '../../../mol-model/structure';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'pdbe-structure-quality-report-prop',
@@ -48,11 +48,11 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.managers.lociLabels.addProvider(this.labelPDBeValidation);
this.ctx.representation.structure.themes.colorThemeRegistry.add(StructureQualityReportColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.add(StructureQualityReportColorThemeProvider);
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach
let updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.params.showTooltip = p.showTooltip;
this.ctx.customModelProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
@@ -62,7 +62,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
unregister() {
this.ctx.customModelProperties.unregister(StructureQualityReportProvider.descriptor.name);
this.ctx.managers.lociLabels.removeProvider(this.labelPDBeValidation);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(StructureQualityReportColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.remove(StructureQualityReportColorThemeProvider);
}
},
params: () => ({

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StructureQualityReport, StructureQualityReportProvider } from '../../../mol-model-props/pdbe/structure-quality-report';
import { StructureQualityReport, StructureQualityReportProvider } from './prop';
import { Location } from '../../../mol-model/location';
import { StructureElement } from '../../../mol-model/structure';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
@@ -12,7 +12,7 @@ import { ThemeDataContext } from '../../../mol-theme/theme';
import { Color } from '../../../mol-util/color';
import { TableLegend } from '../../../mol-util/legend';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { CustomProperty } from '../../common/custom-property';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
const ValidationColors = [
Color.fromRgb(170, 170, 170), // not applicable
@@ -20,7 +20,7 @@ const ValidationColors = [
Color.fromRgb(255, 255, 0), // 1
Color.fromRgb(255, 128, 0), // 2
Color.fromRgb(255, 0, 0), // 3 or more
]
];
const ValidationColorTable: [string, Color][] = [
['No Issues', ValidationColors[1]],
@@ -28,7 +28,7 @@ const ValidationColorTable: [string, Color][] = [
['Two Issues', ValidationColors[3]],
['Three Or More Issues', ValidationColors[4]],
['Not Applicable', ValidationColors[9]]
]
];
export const StructureQualityReportColorThemeParams = {
type: PD.MappedStatic('issue-count', {
@@ -42,7 +42,7 @@ export const StructureQualityReportColorThemeParams = {
type Params = typeof StructureQualityReportColorThemeParams
export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: PD.Values<Params>): ColorTheme<Params> {
let color: LocationColor
let color: LocationColor;
if (ctx.structure && !ctx.structure.isEmpty && ctx.structure.models[0].customProperties.has(StructureQualityReportProvider.descriptor)) {
const getIssues = StructureQualityReport.getIssues;
@@ -53,7 +53,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
return ValidationColors[Math.min(3, getIssues(location).length) + 1];
}
return ValidationColors[0];
}
};
} else {
const issue = props.type.params.kind;
color = (location: Location) => {
@@ -61,7 +61,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
return ValidationColors[4];
}
return ValidationColors[0];
}
};
}
} else {
color = () => ValidationColors[0];
@@ -74,7 +74,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
props: props,
description: 'Assigns residue colors according to the number of quality issues or a specific quality issue. Data from wwPDB Validation Report, obtained via PDBe.',
legend: TableLegend(ValidationColorTable)
}
};
}
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
@@ -107,4 +107,4 @@ export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Param
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(StructureQualityReportProvider.descriptor, false)
}
}
};

View File

@@ -5,24 +5,25 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Column, Table } from '../../mol-data/db';
import { toTable } from '../../mol-io/reader/cif/schema';
import { mmCIF_residueId_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
import { CifWriter } from '../../mol-io/writer/cif';
import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../mol-model/structure';
import { residueIdFields } from '../../mol-model/structure/export/categories/atom_site';
import { StructureElement, CifExportContext, Structure } from '../../mol-model/structure/structure';
import { CustomPropSymbol } from '../../mol-script/language/symbol';
import Type from '../../mol-script/language/type';
import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
import { PropertyWrapper } from '../common/wrapper';
import { CustomModelProperty } from '../common/custom-model-property';
import { ParamDefinition as PD } from '../../mol-util/param-definition'
import { CustomProperty } from '../common/custom-property';
import { arraySetAdd } from '../../mol-util/array';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { Column, Table } from '../../../mol-data/db';
import { toTable } from '../../../mol-io/reader/cif/schema';
import { mmCIF_residueId_schema } from '../../../mol-io/reader/cif/schema/mmcif-extras';
import { CifWriter } from '../../../mol-io/writer/cif';
import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
import { residueIdFields } from '../../../mol-model/structure/export/categories/atom_site';
import { StructureElement, CifExportContext, Structure } from '../../../mol-model/structure/structure';
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
import Type from '../../../mol-script/language/type';
import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { arraySetAdd } from '../../../mol-util/array';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
import { Asset } from '../../../mol-util/assets';
export { StructureQualityReport }
export { StructureQualityReport };
type StructureQualityReport = PropertyWrapper<{
issues: IndexedCustomProperty.Residue<string[]>,
@@ -30,18 +31,13 @@ type StructureQualityReport = PropertyWrapper<{
}| undefined>
namespace StructureQualityReport {
export const DefaultServerUrl = 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/'
export const DefaultServerUrl = 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/';
export function getEntryUrl(pdbId: string, serverUrl: string) {
return `${serverUrl}/${pdbId.toLowerCase()}`
return `${serverUrl}/${pdbId.toLowerCase()}`;
}
export function isApplicable(model?: Model): boolean {
return (
!!model &&
MmcifFormat.is(model.sourceData) &&
(model.sourceData.data.db.database_2.database_id.isDefined ||
model.entryId.length === 4)
)
return !!model && Model.isFromPdbArchive(model);
}
export const Schema = {
@@ -64,27 +60,28 @@ namespace StructureQualityReport {
export function fromJson(model: Model, data: any) {
const info = PropertyWrapper.createInfo();
const issueMap = createIssueMapFromJson(model, data);
return { info, data: issueMap }
return { info, data: issueMap };
}
export async function fromServer(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): Promise<StructureQualityReport> {
const url = getEntryUrl(model.entryId, props.serverUrl)
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
const data = json[model.entryId.toLowerCase()];
export async function fromServer(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): Promise<CustomProperty.Data<StructureQualityReport>> {
const url = Asset.getUrlAsset(ctx.assetManager, getEntryUrl(model.entryId, props.serverUrl));
const json = await ctx.assetManager.resolve(url, 'json').runInContext(ctx.runtime);
const data = json.data[model.entryId.toLowerCase()];
if (!data) throw new Error('missing data');
return fromJson(model, data)
return { value: fromJson(model, data), assets: [json] };
}
export function fromCif(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): StructureQualityReport | undefined {
let info = PropertyWrapper.tryGetInfoFromCif('pdbe_structure_quality_report', model);
if (!info) return
if (!info) return;
const data = getCifData(model);
const issueMap = createIssueMapFromCif(model, data.residues, data.groups);
return { info, data: issueMap }
return { info, data: issueMap };
}
export async function fromCifOrServer(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): Promise<StructureQualityReport> {
return fromCif(ctx, model, props) || fromServer(ctx, model, props)
export async function fromCifOrServer(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): Promise<CustomProperty.Data<StructureQualityReport>> {
const cif = fromCif(ctx, model, props);
return cif ? { value: cif } : fromServer(ctx, model, props);
}
const _emptyArray: string[] = [];
@@ -108,13 +105,13 @@ namespace StructureQualityReport {
return {
residues: toTable(Schema.pdbe_structure_quality_report_issues, model.sourceData.data.frame.categories.pdbe_structure_quality_report_issues),
groups: toTable(Schema.pdbe_structure_quality_report_issue_types, model.sourceData.data.frame.categories.pdbe_structure_quality_report_issue_types),
}
};
}
}
export const StructureQualityReportParams = {
serverUrl: PD.Text(StructureQualityReport.DefaultServerUrl, { description: 'JSON API Server URL' })
}
};
export type StructureQualityReportParams = typeof StructureQualityReportParams
export type StructureQualityReportProps = PD.Values<StructureQualityReportParams>
@@ -135,7 +132,7 @@ export const StructureQualityReportProvider: CustomModelProperty.Provider<Struct
return {
fields: _structure_quality_report_issues_fields,
source: ctx.models.map(data => ({ data, rowCount: data.elements.length }))
}
};
}
}, {
name: 'pdbe_structure_quality_report_issue_types',
@@ -155,10 +152,10 @@ export const StructureQualityReportProvider: CustomModelProperty.Provider<Struct
getParams: (data: Model) => StructureQualityReportParams,
isApplicable: (data: Model) => StructureQualityReport.isApplicable(data),
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<StructureQualityReportProps>) => {
const p = { ...PD.getDefaultValues(StructureQualityReportParams), ...props }
return await StructureQualityReport.fromCifOrServer(ctx, data, p)
const p = { ...PD.getDefaultValues(StructureQualityReportParams), ...props };
return await StructureQualityReport.fromCifOrServer(ctx, data, p);
}
})
});
const _structure_quality_report_issues_fields = CifWriter.fields<number, ReportExportContext['models'][0]>()
.index('id')
@@ -210,7 +207,7 @@ function createExportContext(ctx: CifExportContext): ReportExportContext {
info,
models,
issueTypes: Table.ofArrays(StructureQualityReport.Schema.pdbe_structure_quality_report_issue_types, { group_id, issue_type })
}
};
}
function createIssueMapFromJson(modelData: Model, data: any): StructureQualityReport['data'] | undefined {
@@ -266,7 +263,7 @@ function createIssueMapFromCif(modelData: Model,
for (const t of issues) {
arraySetAdd(issueTypes, t);
}
})
});
return {
issues: IndexedCustomProperty.fromResidueMap(ret),

View File

@@ -0,0 +1,4 @@
### Code generation
**GraphQL schemas**
./node_modules/.bin/graphql-codegen -c ./src/extensions/rcsb/graphql/codegen.yml

View File

@@ -4,20 +4,20 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../../../mol-util/param-definition'
import { AssemblySymmetryProvider, AssemblySymmetry } from '../../../../../mol-model-props/rcsb/assembly-symmetry';
import { PluginBehavior } from '../../../behavior';
import { AssemblySymmetryParams, AssemblySymmetryRepresentation } from '../../../../../mol-model-props/rcsb/representations/assembly-symmetry';
import { AssemblySymmetryClusterColorThemeProvider } from '../../../../../mol-model-props/rcsb/themes/assembly-symmetry-cluster';
import { PluginStateTransform, PluginStateObject } from '../../../../../mol-plugin-state/objects';
import { Task } from '../../../../../mol-task';
import { PluginContext } from '../../../../context';
import { StateTransformer, StateAction, StateObject, StateTransform, StateObjectRef } from '../../../../../mol-state';
import { GenericRepresentationRef } from '../../../../../mol-plugin-state/manager/structure/hierarchy-state';
import { AssemblySymmetryControls } from './ui/assembly-symmetry';
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../../../mol-plugin-state/builder/structure/representation-preset';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { AssemblySymmetryProvider, AssemblySymmetry, AssemblySymmetryDataProvider } from './prop';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
import { AssemblySymmetryParams, AssemblySymmetryRepresentation } from './representation';
import { AssemblySymmetryClusterColorThemeProvider } from './color';
import { PluginStateTransform, PluginStateObject } from '../../../mol-plugin-state/objects';
import { Task } from '../../../mol-task';
import { PluginContext } from '../../../mol-plugin/context';
import { StateTransformer, StateAction, StateObject, StateTransform, StateObjectRef } from '../../../mol-state';
import { GenericRepresentationRef } from '../../../mol-plugin-state/manager/structure/hierarchy-state';
import { AssemblySymmetryControls } from './ui';
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
const Tag = AssemblySymmetry.Tag
const Tag = AssemblySymmetry.Tag;
export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({
name: 'rcsb-assembly-symmetry-prop',
@@ -30,37 +30,37 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
private provider = AssemblySymmetryProvider
register(): void {
this.ctx.state.data.actions.add(InitAssemblySymmetry3D)
this.ctx.state.data.actions.add(InitAssemblySymmetry3D);
this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
this.ctx.representation.structure.themes.colorThemeRegistry.add(AssemblySymmetryClusterColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.add(AssemblySymmetryClusterColorThemeProvider);
this.ctx.genericRepresentationControls.set(Tag.Representation, selection => {
const refs: GenericRepresentationRef[] = []
const refs: GenericRepresentationRef[] = [];
selection.structures.forEach(structure => {
const symmRepr = structure.genericRepresentations?.filter(r => r.cell.transform.transformer.id === AssemblySymmetry3D.id)[0]
if (symmRepr) refs.push(symmRepr)
})
return [refs, 'Symmetries']
})
this.ctx.customStructureControls.set(Tag.Representation, AssemblySymmetryControls as any)
this.ctx.builders.structure.representation.registerPreset(AssemblySymmetryPreset)
const symmRepr = structure.genericRepresentations?.filter(r => r.cell.transform.transformer.id === AssemblySymmetry3D.id)[0];
if (symmRepr) refs.push(symmRepr);
});
return [refs, 'Symmetries'];
});
this.ctx.customStructureControls.set(Tag.Representation, AssemblySymmetryControls as any);
this.ctx.builders.structure.representation.registerPreset(AssemblySymmetryPreset);
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach
let updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
}
unregister() {
this.ctx.state.data.actions.remove(InitAssemblySymmetry3D)
this.ctx.state.data.actions.remove(InitAssemblySymmetry3D);
this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(AssemblySymmetryClusterColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.remove(AssemblySymmetryClusterColorThemeProvider);
this.ctx.genericRepresentationControls.delete(Tag.Representation)
this.ctx.customStructureControls.delete(Tag.Representation)
this.ctx.builders.structure.representation.unregisterPreset(AssemblySymmetryPreset)
this.ctx.genericRepresentationControls.delete(Tag.Representation);
this.ctx.customStructureControls.delete(Tag.Representation);
this.ctx.builders.structure.representation.unregisterPreset(AssemblySymmetryPreset);
}
},
params: () => ({
@@ -80,16 +80,21 @@ export const InitAssemblySymmetry3D = StateAction.build({
isApplicable: (a) => AssemblySymmetry.isApplicable(a.data)
})(({ a, ref, state }, plugin: PluginContext) => Task.create('Init Assembly Symmetry', async ctx => {
try {
await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
const propCtx = { runtime: ctx, assetManager: plugin.managers.asset };
await AssemblySymmetryDataProvider.attach(propCtx, a.data);
const assemblySymmetryData = AssemblySymmetryDataProvider.get(a.data).value;
const symmetryIndex = assemblySymmetryData ? AssemblySymmetry.firstNonC1(assemblySymmetryData) : -1;
await AssemblySymmetryProvider.attach(propCtx, a.data, { symmetryIndex });
} catch(e) {
plugin.log.error(`Assembly Symmetry: ${e}`)
return
plugin.log.error(`Assembly Symmetry: ${e}`);
return;
}
const tree = state.build().to(ref).apply(AssemblySymmetry3D);
const tree = state.build().to(ref)
.applyOrUpdateTagged(AssemblySymmetry.Tag.Representation, AssemblySymmetry3D);
await state.updateTree(tree).runInContext(ctx);
}));
export { AssemblySymmetry3D }
export { AssemblySymmetry3D };
type AssemblySymmetry3D = typeof AssemblySymmetry3D
const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
@@ -103,7 +108,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
params: (a) => {
return {
...AssemblySymmetryParams,
}
};
}
})({
canAutoUpdate({ oldParams, newParams }) {
@@ -111,43 +116,44 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
},
apply({ a, params }, plugin: PluginContext) {
return Task.create('Assembly Symmetry', async ctx => {
await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value
await AssemblySymmetryProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value;
if (!assemblySymmetry || assemblySymmetry.symbol === 'C1') {
return StateObject.Null;
}
const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AssemblySymmetryParams)
const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AssemblySymmetryParams);
await repr.createOrUpdate(params, a.data).runInContext(ctx);
const { type, kind, symbol } = assemblySymmetry
const { type, kind, symbol } = assemblySymmetry;
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: kind, description: `${type} (${symbol})` });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
return Task.create('Assembly Symmetry', async ctx => {
await AssemblySymmetryProvider.attach({ runtime: ctx, fetch: plugin.fetch }, a.data)
const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value
await AssemblySymmetryProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value;
if (!assemblySymmetry || assemblySymmetry.symbol === 'C1') {
return StateTransformer.UpdateResult.Recreate
// this should NOT be StateTransformer.UpdateResult.Null
// because that keeps the old object
return StateTransformer.UpdateResult.Recreate;
}
const props = { ...b.data.repr.props, ...newParams }
const props = { ...b.data.repr.props, ...newParams };
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
const { type, kind, symbol } = assemblySymmetry
b.label = kind
b.description = `${type} (${symbol})`
const { type, kind, symbol } = assemblySymmetry;
b.label = kind;
b.description = `${type} (${symbol})`;
return StateTransformer.UpdateResult.Updated;
});
},
isApplicable(a) {
return AssemblySymmetry.isApplicable(a.data)
return AssemblySymmetry.isApplicable(a.data);
}
});
//
const AssemblySymmetryPresetParams = {
export const AssemblySymmetryPresetParams = {
...StructureRepresentationPresetProvider.CommonParams,
symmetryIndex: PD.Numeric(0)
}
};
export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-rcsb-assembly-symmetry',
@@ -156,7 +162,7 @@ export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
description: 'Shows Assembly Symmetry axes and cage; colors structure according to assembly symmetry cluster membership. Data calculated with BioJava, obtained via RCSB PDB.'
},
isApplicable(a) {
return AssemblySymmetry.isApplicable(a.data)
return AssemblySymmetry.isApplicable(a.data);
},
params: () => AssemblySymmetryPresetParams,
async apply(ref, params, plugin) {
@@ -164,21 +170,27 @@ export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
await plugin.runTask(Task.create('Assembly Symmetry', async runtime => {
await AssemblySymmetryProvider.attach({ fetch: plugin.fetch, runtime }, structure, { symmetryIndex: params.symmetryIndex })
}))
if (!AssemblySymmetryDataProvider.get(structure).value) {
await plugin.runTask(Task.create('Assembly Symmetry', async runtime => {
const propCtx = { runtime, assetManager: plugin.managers.asset };
await AssemblySymmetryDataProvider.attach(propCtx, structure);
const assemblySymmetryData = AssemblySymmetryDataProvider.get(structure).value;
const symmetryIndex = assemblySymmetryData ? AssemblySymmetry.firstNonC1(assemblySymmetryData) : -1;
await AssemblySymmetryProvider.attach(propCtx, structure, { symmetryIndex });
}));
}
const assemblySymmetry = await tryCreateAssemblySymmetry(plugin, structureCell);
const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin);
const globalThemeName = assemblySymmetry.isOk ? Tag.Cluster as any : undefined;
const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName }, plugin);
return { components: preset.components, representations: { ...preset.representations, assemblySymmetry } };
}
});
async function tryCreateAssemblySymmetry(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, params?: StateTransformer.Params<AssemblySymmetry3D>, initialState?: Partial<StateTransform.State>) {
export function tryCreateAssemblySymmetry(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, params?: StateTransformer.Params<AssemblySymmetry3D>, initialState?: Partial<StateTransform.State>) {
const state = plugin.state.data;
const assemblySymmetry = state.build().to(structure)
.apply(AssemblySymmetry3D, params, { state: initialState });
await plugin.updateDataState(assemblySymmetry, { revertOnError: true });
return assemblySymmetry.selector
.applyOrUpdateTagged(AssemblySymmetry.Tag.Representation, AssemblySymmetry3D, params, { state: initialState });
return assemblySymmetry.commit({ revertOnError: true });
}

View File

@@ -6,76 +6,76 @@
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { AssemblySymmetryProvider, AssemblySymmetry } from '../assembly-symmetry';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { AssemblySymmetryProvider, AssemblySymmetry } from './prop';
import { Color } from '../../../mol-util/color';
import { Unit, StructureElement, StructureProperties } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
import { getPalette, getPaletteParams } from '../../../mol-util/color/palette';
import { CustomProperty } from '../../common/custom-property';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
const DefaultColor = Color(0xCCCCCC)
const DefaultColor = Color(0xCCCCCC);
function getAsymId(unit: Unit): StructureElement.Property<string> {
switch (unit.kind) {
case Unit.Kind.Atomic:
return StructureProperties.chain.label_asym_id
return StructureProperties.chain.label_asym_id;
case Unit.Kind.Spheres:
case Unit.Kind.Gaussians:
return StructureProperties.coarse.asym_id
return StructureProperties.coarse.asym_id;
}
}
function clusterMemberKey(asymId: string, operList: string[]) {
return `${asymId}-${operList.join('|')}`
return `${asymId}-${operList.join('|')}`;
}
export const AssemblySymmetryClusterColorThemeParams = {
...getPaletteParams({ colorList: 'red-yellow-blue' }),
}
};
export type AssemblySymmetryClusterColorThemeParams = typeof AssemblySymmetryClusterColorThemeParams
export function getAssemblySymmetryClusterColorThemeParams(ctx: ThemeDataContext) {
const params = PD.clone(AssemblySymmetryClusterColorThemeParams)
return params
const params = PD.clone(AssemblySymmetryClusterColorThemeParams);
return params;
}
export function AssemblySymmetryClusterColorTheme(ctx: ThemeDataContext, props: PD.Values<AssemblySymmetryClusterColorThemeParams>): ColorTheme<AssemblySymmetryClusterColorThemeParams> {
let color: LocationColor = () => DefaultColor
let legend: ScaleLegend | TableLegend | undefined
let color: LocationColor = () => DefaultColor;
let legend: ScaleLegend | TableLegend | undefined;
const assemblySymmetry = ctx.structure && AssemblySymmetryProvider.get(ctx.structure)
const contextHash = assemblySymmetry?.version
const assemblySymmetry = ctx.structure && AssemblySymmetryProvider.get(ctx.structure);
const contextHash = assemblySymmetry?.version;
const clusters = assemblySymmetry?.value?.clusters
const clusters = assemblySymmetry?.value?.clusters;
if (clusters?.length && ctx.structure) {
const clusterByMember = new Map<string, number>()
const clusterByMember = new Map<string, number>();
for (let i = 0, il = clusters.length; i < il; ++i) {
const { members } = clusters[i]!
const { members } = clusters[i]!;
for (let j = 0, jl = members.length; j < jl; ++j) {
const asymId = members[j]!.asym_id
const operList = [...members[j]!.pdbx_struct_oper_list_ids || []] as string[]
clusterByMember.set(clusterMemberKey(asymId, operList), i)
const asymId = members[j]!.asym_id;
const operList = [...members[j]!.pdbx_struct_oper_list_ids || []] as string[];
clusterByMember.set(clusterMemberKey(asymId, operList), i);
if (operList.length === 0) {
operList.push('1') // TODO hack assuming '1' is the id of the identity operator
clusterByMember.set(clusterMemberKey(asymId, operList), i)
operList.push('1'); // TODO hack assuming '1' is the id of the identity operator
clusterByMember.set(clusterMemberKey(asymId, operList), i);
}
}
}
const palette = getPalette(clusters.length, props)
legend = palette.legend
const palette = getPalette(clusters.length, props);
legend = palette.legend;
const _emptyList: any[] = [];
color = (location: Location): Color => {
if (StructureElement.Location.is(location)) {
const { assembly } = location.unit.conformation.operator
const asymId = getAsymId(location.unit)(location)
const cluster = clusterByMember.get(clusterMemberKey(asymId, assembly?.operList || _emptyList))
return cluster !== undefined ? palette.color(cluster) : DefaultColor
const { assembly } = location.unit.conformation.operator;
const asymId = getAsymId(location.unit)(location);
const cluster = clusterByMember.get(clusterMemberKey(asymId, assembly?.operList || _emptyList));
return cluster !== undefined ? palette.color(cluster) : DefaultColor;
}
return DefaultColor
}
return DefaultColor;
};
}
return {
@@ -86,7 +86,7 @@ export function AssemblySymmetryClusterColorTheme(ctx: ThemeDataContext, props:
contextHash,
description: 'Assigns chain colors according to assembly symmetry cluster membership calculated with BioJava and obtained via RCSB PDB.',
legend
}
};
}
export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<AssemblySymmetryClusterColorThemeParams, AssemblySymmetry.Tag.Cluster> = {
@@ -101,4 +101,4 @@ export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<Asse
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? AssemblySymmetryProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.customPropertyDescriptors.reference(AssemblySymmetryProvider.descriptor, false)
}
}
};

View File

@@ -4,18 +4,21 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from './graphql/types';
import query from './graphql/symmetry.gql';
import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from '../graphql/types';
import query from '../graphql/symmetry.gql';
import { ParamDefinition as PD } from '../../mol-util/param-definition'
import { CustomPropertyDescriptor, Structure, Model } from '../../mol-model/structure';
import { Database as _Database, Column } from '../../mol-data/db'
import { GraphQLClient } from '../../mol-util/graphql-client';
import { CustomProperty } from '../common/custom-property';
import { NonNullableArray } from '../../mol-util/type-helpers';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { ReadonlyVec3 } from '../../mol-math/linear-algebra/3d/vec3';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { CustomPropertyDescriptor, Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
import { Database as _Database, Column } from '../../../mol-data/db';
import { GraphQLClient } from '../../../mol-util/graphql-client';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { NonNullableArray } from '../../../mol-util/type-helpers';
import { CustomStructureProperty } from '../../../mol-model-props/common/custom-structure-property';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
import { ReadonlyVec3 } from '../../../mol-math/linear-algebra/3d/vec3';
import { SetUtils } from '../../../mol-util/set';
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
import { compile } from '../../../mol-script/runtime/query/compiler';
const BiologicalAssemblyNames = new Set([
'author_and_software_defined_assembly',
@@ -24,18 +27,18 @@ const BiologicalAssemblyNames = new Set([
'complete point assembly',
'representative helical assembly',
'software_defined_assembly'
])
]);
export function isBiologicalAssembly(structure: Structure): boolean {
if (!MmcifFormat.is(structure.models[0].sourceData)) return false
const mmcif = structure.models[0].sourceData.data.db
if (!mmcif.pdbx_struct_assembly.details.isDefined) return false
const id = structure.units[0].conformation.operator.assembly?.id || ''
if (id === '' || id === 'deposited') return true
const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id)
if (indices.length !== 1) return false
const details = mmcif.pdbx_struct_assembly.details.value(indices[0])
return BiologicalAssemblyNames.has(details)
if (!MmcifFormat.is(structure.models[0].sourceData)) return false;
const mmcif = structure.models[0].sourceData.data.db;
if (!mmcif.pdbx_struct_assembly.details.isDefined) return false;
const id = structure.units[0].conformation.operator.assembly?.id || '';
if (id === '' || id === 'deposited') return true;
const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id);
if (indices.length !== 1) return false;
const details = mmcif.pdbx_struct_assembly.details.value(indices[0]);
return BiologicalAssemblyNames.has(details);
}
export namespace AssemblySymmetry {
@@ -44,65 +47,103 @@ export namespace AssemblySymmetry {
Representation = 'rcsb-assembly-symmetry-3d'
}
export const DefaultServerUrl = 'https://data-beta.rcsb.org/graphql'
export const DefaultServerUrl = 'https://data.rcsb.org/graphql';
export function isApplicable(structure?: Structure): boolean {
return (
!!structure && structure.models.length === 1 &&
Model.isFromPdbArchive(structure.models[0]) &&
isBiologicalAssembly(structure)
)
);
}
export async function fetch(ctx: CustomProperty.Context, structure: Structure, props: AssemblySymmetryDataProps): Promise<AssemblySymmetryDataValue> {
if (!isApplicable(structure)) return []
export async function fetch(ctx: CustomProperty.Context, structure: Structure, props: AssemblySymmetryDataProps): Promise<CustomProperty.Data<AssemblySymmetryDataValue>> {
if (!isApplicable(structure)) return { value: [] };
const client = new GraphQLClient(props.serverUrl, ctx.fetch)
const client = new GraphQLClient(props.serverUrl, ctx.assetManager);
const variables: AssemblySymmetryQueryVariables = {
assembly_id: structure.units[0].conformation.operator.assembly?.id || 'deposited',
entry_id: structure.units[0].model.entryId
}
const result = await client.request<AssemblySymmetryQuery>(ctx.runtime, query, variables)
};
const result = await client.request(ctx.runtime, query, variables);
let value: AssemblySymmetryDataValue = [];
if (!result.assembly?.rcsb_struct_symmetry) {
console.error('expected `rcsb_struct_symmetry` field')
return []
if (!result.data.assembly?.rcsb_struct_symmetry) {
console.error('expected `rcsb_struct_symmetry` field');
} else {
value = result.data.assembly.rcsb_struct_symmetry as AssemblySymmetryDataValue;
}
return result.assembly.rcsb_struct_symmetry as AssemblySymmetryDataValue
return { value, assets: [result] };
}
/** Returns the index of the first non C1 symmetry or -1 */
export function firstNonC1(assemblySymmetryData: AssemblySymmetryDataValue) {
for (let i = 0, il = assemblySymmetryData.length; i < il; ++i) {
if (assemblySymmetryData[i].symbol !== 'C1') return i;
}
return -1;
}
export type RotationAxes = ReadonlyArray<{ order: number, start: ReadonlyVec3, end: ReadonlyVec3 }>
export function isRotationAxes(x: AssemblySymmetryValue['rotation_axes']): x is RotationAxes {
return !!x && x.length > 0
return !!x && x.length > 0;
}
export function getAsymIds(assemblySymmetry: AssemblySymmetryValue) {
const asymIds = new Set<string>();
for (const c of assemblySymmetry.clusters) {
if (!c?.members) continue;
for (const m of c.members) {
if (m?.asym_id) asymIds.add(m.asym_id);
}
}
return SetUtils.toArray(asymIds);
}
function getAsymIdsStructure(structure: Structure, asymIds: string[]) {
const query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('label_asym_id')])
})
]);
const compiled = compile<StructureSelection>(query);
const result = compiled(new QueryContext(structure));
return StructureSelection.unionStructure(result);
}
/** Returns structure limited to all cluster member chains */
export function getStructure(structure: Structure, assemblySymmetry: AssemblySymmetryValue) {
const asymIds = AssemblySymmetry.getAsymIds(assemblySymmetry);
return asymIds.length > 0 ? getAsymIdsStructure(structure, asymIds) : structure;
}
}
export function getSymmetrySelectParam(structure?: Structure) {
const param = PD.Select<number>(0, [[0, 'First Symmetry']])
const param = PD.Select<number>(0, [[0, 'First Symmetry']]);
if (structure) {
const assemblySymmetryData = AssemblySymmetryDataProvider.get(structure).value
const assemblySymmetryData = AssemblySymmetryDataProvider.get(structure).value;
if (assemblySymmetryData) {
const options: [number, string][] = []
const options: [number, string][] = [];
for (let i = 0, il = assemblySymmetryData.length; i < il; ++i) {
const { symbol, kind } = assemblySymmetryData[i]
const { symbol, kind } = assemblySymmetryData[i];
if (symbol !== 'C1') {
options.push([ i, `${i + 1}: ${symbol} ${kind}` ])
options.push([ i, `${i + 1}: ${symbol} ${kind}` ]);
}
}
if (options.length) {
param.options = options
param.defaultValue = options[0][0]
param.options = options;
param.defaultValue = options[0][0];
}
}
}
return param
return param;
}
//
export const AssemblySymmetryDataParams = {
serverUrl: PD.Text(AssemblySymmetry.DefaultServerUrl, { description: 'GraphQL endpoint URL' })
}
};
export type AssemblySymmetryDataParams = typeof AssemblySymmetryDataParams
export type AssemblySymmetryDataProps = PD.Values<AssemblySymmetryDataParams>
@@ -119,10 +160,10 @@ export const AssemblySymmetryDataProvider: CustomStructureProperty.Provider<Asse
getParams: (data: Structure) => AssemblySymmetryDataParams,
isApplicable: (data: Structure) => AssemblySymmetry.isApplicable(data),
obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<AssemblySymmetryDataProps>) => {
const p = { ...PD.getDefaultValues(AssemblySymmetryDataParams), ...props }
return await AssemblySymmetry.fetch(ctx, data, p)
const p = { ...PD.getDefaultValues(AssemblySymmetryDataParams), ...props };
return await AssemblySymmetry.fetch(ctx, data, p);
}
})
});
//
@@ -130,10 +171,10 @@ function getAssemblySymmetryParams(data?: Structure) {
return {
... AssemblySymmetryDataParams,
symmetryIndex: getSymmetrySelectParam(data)
}
};
}
export const AssemblySymmetryParams = getAssemblySymmetryParams()
export const AssemblySymmetryParams = getAssemblySymmetryParams();
export type AssemblySymmetryParams = typeof AssemblySymmetryParams
export type AssemblySymmetryProps = PD.Values<AssemblySymmetryParams>
@@ -150,11 +191,11 @@ export const AssemblySymmetryProvider: CustomStructureProperty.Provider<Assembly
getParams: getAssemblySymmetryParams,
isApplicable: (data: Structure) => AssemblySymmetry.isApplicable(data),
obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<AssemblySymmetryProps>) => {
const p = { ...PD.getDefaultValues(getAssemblySymmetryParams(data)), ...props }
await AssemblySymmetryDataProvider.attach(ctx, data, p)
const assemblySymmetryData = AssemblySymmetryDataProvider.get(data).value
const assemblySymmetry = assemblySymmetryData?.[p.symmetryIndex]
if (!assemblySymmetry) new Error(`No assembly symmetry found for index ${p.symmetryIndex}`)
return assemblySymmetry
const p = { ...PD.getDefaultValues(getAssemblySymmetryParams(data)), ...props };
await AssemblySymmetryDataProvider.attach(ctx, data, p);
const assemblySymmetryData = AssemblySymmetryDataProvider.get(data).value;
const assemblySymmetry = assemblySymmetryData?.[p.symmetryIndex];
if (!assemblySymmetry) new Error(`No assembly symmetry found for index ${p.symmetryIndex}`);
return { value: assemblySymmetry };
}
})
});

View File

@@ -5,9 +5,9 @@
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { AssemblySymmetryValue, AssemblySymmetryProvider, AssemblySymmetry } from '../assembly-symmetry';
import { AssemblySymmetryValue, AssemblySymmetryProvider, AssemblySymmetry } from './prop';
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
import { RuntimeContext } from '../../../mol-task';
@@ -32,26 +32,27 @@ import { Mutable } from '../../../mol-util/type-helpers';
import { equalEps } from '../../../mol-math/linear-algebra/3d/common';
import { Structure } from '../../../mol-model/structure';
import { isInteger } from '../../../mol-util/number';
import { Sphere3D } from '../../../mol-math/geometry';
const OrderColors = ColorMap({
'2': ColorNames.deepskyblue,
'3': ColorNames.lime,
'N': ColorNames.red,
})
});
const OrderColorsLegend = TableLegend(Object.keys(OrderColors).map(name => {
return [name, (OrderColors as any)[name] as Color] as [string, Color]
}))
return [name, (OrderColors as any)[name] as Color] as [string, Color];
}));
function axesColorHelp(value: { name: string, params: {} }) {
return value.name === 'byOrder'
? { description: 'Color axes by their order', legend: OrderColorsLegend }
: {}
: {};
}
const SharedParams = {
...Mesh.Params,
scale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
}
};
const AxesParams = {
...SharedParams,
@@ -61,121 +62,122 @@ const AxesParams = {
colorValue: PD.Color(ColorNames.orange),
}, { isFlat: true })
}, { help: axesColorHelp }),
}
};
type AxesParams = typeof AxesParams
const CageParams = {
...SharedParams,
cageColor: PD.Color(ColorNames.orange),
}
};
type CageParams = typeof CageParams
const AssemblySymmetryVisuals = {
'axes': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, AxesParams>) => ShapeRepresentation(getAxesShape, Mesh.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
// cage should come before 'axes' so that the representative loci uses the cage shape
'cage': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, CageParams>) => ShapeRepresentation(getCageShape, Mesh.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
}
'axes': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, AxesParams>) => ShapeRepresentation(getAxesShape, Mesh.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
};
export const AssemblySymmetryParams = {
...AxesParams,
...CageParams,
visuals: PD.MultiSelect(['axes', 'cage'], PD.objectToOptions(AssemblySymmetryVisuals)),
}
};
export type AssemblySymmetryParams = typeof AssemblySymmetryParams
export type AssemblySymmetryProps = PD.Values<AssemblySymmetryParams>
//
function getAssemblyName(s: Structure) {
const id = s.units[0].conformation.operator.assembly?.id || ''
return isInteger(id) ? `Assembly ${id}` : id
const id = s.units[0].conformation.operator.assembly?.id || '';
return isInteger(id) ? `Assembly ${id}` : id;
}
const t = Mat4.identity()
const tmpV = Vec3()
const tmpCenter = Vec3()
const tmpScale = Vec3()
const t = Mat4.identity();
const tmpV = Vec3();
const tmpCenter = Vec3();
const tmpScale = Vec3();
const getOrderPrimitive = memoize1((order: number): Primitive | undefined => {
if (order < 2) {
return Prism(polygon(48, false))
return Prism(polygon(48, false));
} else if (order === 2) {
const lens = Prism(polygon(48, false))
const m = Mat4.identity()
Mat4.scale(m, m, Vec3.create(1, 0.35, 1))
transformPrimitive(lens, m)
return lens
const lens = Prism(polygon(48, false));
const m = Mat4.identity();
Mat4.scale(m, m, Vec3.create(1, 0.35, 1));
transformPrimitive(lens, m);
return lens;
} else if (order === 3) {
return Wedge()
return Wedge();
} else {
return Prism(polygon(order, false))
return Prism(polygon(order, false));
}
})
});
function getAxesMesh(data: AssemblySymmetryValue, props: PD.Values<AxesParams>, mesh?: Mesh) {
const { scale } = props
const { scale } = props;
const { rotation_axes } = data
if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh)
const { rotation_axes } = data;
if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh);
const { start, end } = rotation_axes[0]
const radius = (Vec3.distance(start, end) / 500) * scale
const { start, end } = rotation_axes[0];
const radius = (Vec3.distance(start, end) / 500) * scale;
Vec3.set(tmpScale, radius * 7, radius * 7, radius * 0.4)
Vec3.set(tmpScale, radius * 7, radius * 7, radius * 0.4);
const cylinderProps = { radiusTop: radius, radiusBottom: radius }
const builderState = MeshBuilder.createState(256, 128, mesh)
const cylinderProps = { radiusTop: radius, radiusBottom: radius };
const builderState = MeshBuilder.createState(256, 128, mesh);
builderState.currentGroup = 0
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5)
builderState.currentGroup = 0;
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5);
for (let i = 0, il = rotation_axes.length; i < il; ++i) {
const { order, start, end } = rotation_axes[i]
builderState.currentGroup = i
addCylinder(builderState, start, end, 1, cylinderProps)
const { order, start, end } = rotation_axes[i];
builderState.currentGroup = i;
addCylinder(builderState, start, end, 1, cylinderProps);
const primitive = getOrderPrimitive(order)
const primitive = getOrderPrimitive(order);
if (primitive) {
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5)
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5);
if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, start, tmpCenter)) === 0) {
Mat4.targetTo(t, start, tmpCenter, Vec3.unitY)
Mat4.targetTo(t, start, tmpCenter, Vec3.unitY);
} else {
Mat4.targetTo(t, start, tmpCenter, Vec3.unitX)
Mat4.targetTo(t, start, tmpCenter, Vec3.unitX);
}
Mat4.scale(t, t, tmpScale)
Mat4.scale(t, t, tmpScale);
Mat4.setTranslation(t, start)
MeshBuilder.addPrimitive(builderState, t, primitive)
Mat4.setTranslation(t, end)
MeshBuilder.addPrimitive(builderState, t, primitive)
Mat4.setTranslation(t, start);
MeshBuilder.addPrimitive(builderState, t, primitive);
Mat4.setTranslation(t, end);
MeshBuilder.addPrimitive(builderState, t, primitive);
}
}
return MeshBuilder.getMesh(builderState)
return MeshBuilder.getMesh(builderState);
}
function getAxesShape(ctx: RuntimeContext, data: Structure, props: AssemblySymmetryProps, shape?: Shape<Mesh>) {
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!;
const geo = getAxesMesh(assemblySymmetry, props, shape && shape.geometry);
const getColor = (groupId: number) => {
if (props.axesColor.name === 'byOrder') {
const { rotation_axes } = assemblySymmetry
const order = rotation_axes![groupId]?.order
if (order === 2) return OrderColors[2]
else if (order === 3) return OrderColors[3]
else return OrderColors.N
const { rotation_axes } = assemblySymmetry;
const order = rotation_axes![groupId]?.order;
if (order === 2) return OrderColors[2];
else if (order === 3) return OrderColors[3];
else return OrderColors.N;
} else {
return props.axesColor.params.colorValue
return props.axesColor.params.colorValue;
}
}
};
const getLabel = (groupId: number) => {
const { type, symbol, kind, rotation_axes } = assemblySymmetry
const order = rotation_axes![groupId]?.order
const { type, symbol, kind, rotation_axes } = assemblySymmetry;
const order = rotation_axes![groupId]?.order;
return [
`<small>${data.model.entryId}</small>`,
`<small>${getAssemblyName(data)}</small>`,
`Axis ${groupId + 1} with Order ${order} of ${type} ${kind} (${symbol})`
].join(' | ')
}
return Shape.create('Axes', data, geo, getColor, () => 1, getLabel)
].join(' | ');
};
return Shape.create('Axes', data, geo, getColor, () => 1, getLabel);
}
//
@@ -183,144 +185,178 @@ function getAxesShape(ctx: RuntimeContext, data: Structure, props: AssemblySymme
const getSymbolCage = memoize1((symbol: string): Cage | undefined => {
if (symbol.startsWith('D') || symbol.startsWith('C')) {
// z axis is prism axis, x/y axes cut through edge midpoints
const fold = parseInt(symbol.substr(1))
const fold = parseInt(symbol.substr(1));
if (fold === 2) {
return PrismCage(polygon(4, false))
return PrismCage(polygon(4, false));
} else if (fold === 3) {
return WedgeCage()
return WedgeCage();
} else if (fold > 3) {
return PrismCage(polygon(fold, false))
return PrismCage(polygon(fold, false));
}
} else if (symbol === 'O') {
// x/y/z axes cut through order 4 vertices
return OctahedronCage()
return OctahedronCage();
} else if (symbol === 'I') {
// z axis cut through order 5 vertex
// x axis cut through edge midpoint
const cage = IcosahedronCage()
const m = Mat4.identity()
Mat4.rotate(m, m, degToRad(31.7), Vec3.unitX)
return transformCage(cloneCage(cage), m)
const cage = IcosahedronCage();
const m = Mat4.identity();
Mat4.rotate(m, m, degToRad(31.7), Vec3.unitX);
return transformCage(cloneCage(cage), m);
} else if (symbol === 'T') {
// x/y/z axes cut through edge midpoints
return TetrahedronCage()
return TetrahedronCage();
}
})
});
function getSymbolScale(symbol: string) {
if (symbol.startsWith('D') || symbol.startsWith('C')) {
return 0.75
return 0.75;
} else if (symbol === 'O') {
return 1.2
return 1.2;
} else if (symbol === 'I') {
return 0.25
return 0.25;
} else if (symbol === 'T') {
return 0.8
return 0.8;
}
return 1
return 1;
}
function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.RotationAxes, size: number, structure: Structure) {
const eye = Vec3()
const target = Vec3()
const up = Vec3()
let pair: Mutable<AssemblySymmetry.RotationAxes> | undefined = undefined
const eye = Vec3();
const target = Vec3();
const up = Vec3();
let pair: Mutable<AssemblySymmetry.RotationAxes> | undefined = undefined;
if (symbol.startsWith('C')) {
pair = [axes[0]]
pair = [axes[0]];
} else if (symbol.startsWith('D')) {
const fold = parseInt(symbol.substr(1))
const fold = parseInt(symbol.substr(1));
if (fold === 2) {
pair = axes.filter(a => a.order === 2)
pair = axes.filter(a => a.order === 2);
} else if (fold >= 3) {
const aN = axes.filter(a => a.order === fold)[0]
const a2 = axes.filter(a => a.order === 2)[0]
pair = [aN, a2]
const aN = axes.filter(a => a.order === fold)[0];
const a2 = axes.filter(a => a.order === 2)[0];
pair = [aN, a2];
}
} else if (symbol === 'O') {
pair = axes.filter(a => a.order === 4)
pair = axes.filter(a => a.order === 4);
} else if (symbol === 'I') {
const a5 = axes.filter(a => a.order === 5)[0]
const a5dir = Vec3.sub(Vec3(), a5.end, a5.start)
pair = [a5]
const a5 = axes.filter(a => a.order === 5)[0];
const a5dir = Vec3.sub(Vec3(), a5.end, a5.start);
pair = [a5];
for (const a of axes.filter(a => a.order === 3)) {
let d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir))
if (equalEps(d, 100.81, 0.1)) {
pair[1] = a
break
let d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
if (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1)) {
pair[1] = a;
break;
}
}
} else if (symbol === 'T') {
pair = axes.filter(a => a.order === 2)
pair = axes.filter(a => a.order === 2);
}
Mat4.setIdentity(t)
Mat4.setIdentity(t);
if (pair) {
const [aA, aB] = pair
Vec3.scale(eye, Vec3.add(eye, aA.end, aA.start), 0.5)
Vec3.copy(target, aA.end)
const [aA, aB] = pair;
Vec3.scale(eye, Vec3.add(eye, aA.end, aA.start), 0.5);
Vec3.copy(target, aA.end);
if (aB) {
Vec3.sub(up, aB.end, aB.start)
Mat4.targetTo(t, eye, target, up)
Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol))
Vec3.sub(up, aB.end, aB.start);
const d = Vec3.dot(eye, up);
if (d < 0) Vec3.negate(up, up);
Mat4.targetTo(t, eye, target, up);
Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol));
} else {
if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, aA.end, aA.start)) === 0) {
Vec3.copy(up, Vec3.unitY)
Vec3.copy(up, Vec3.unitY);
} else {
Vec3.copy(up, Vec3.unitX)
Vec3.copy(up, Vec3.unitX);
}
const sizeXY = (structure.lookup3d.boundary.sphere.radius * 2) * 0.8
Mat4.targetTo(t, eye, target, up)
Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, size))
Mat4.targetTo(t, eye, target, up);
const { sphere } = structure.lookup3d.boundary;
let sizeXY = (sphere.radius * 2) * 0.8; // fallback for missing extrema
if (Sphere3D.hasExtrema(sphere)) {
const n = Mat3.directionTransform(Mat3(), t);
const dirs = unitCircleDirections.map(d => Vec3.transformMat3(Vec3(), d, n));
sizeXY = getMaxProjectedDistance(sphere.extrema, dirs, sphere.center);
}
Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, size * 0.9));
}
}
}
const unitCircleDirections = (function() {
const dirs: Vec3[] = [];
const circle = polygon(12, false, 1);
for (let i = 0, il = circle.length; i < il; i += 3) {
dirs.push(Vec3.fromArray(Vec3(), circle, i));
}
return dirs;
})();
const tmpProj = Vec3();
function getMaxProjectedDistance(points: Vec3[], directions: Vec3[], center: Vec3) {
let maxDist = 0;
for (const p of points) {
for (const d of directions) {
Vec3.projectPointOnVector(tmpProj, p, d, center);
const dist = Vec3.distance(tmpProj, center);
if (dist > maxDist) maxDist = dist;
}
}
return maxDist;
}
function getCageMesh(data: Structure, props: PD.Values<CageParams>, mesh?: Mesh) {
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!
const { scale } = props
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!;
const { scale } = props;
const { rotation_axes, symbol } = assemblySymmetry
if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh)
const { rotation_axes, symbol } = assemblySymmetry;
if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh);
const cage = getSymbolCage(symbol)
if (!cage) return Mesh.createEmpty(mesh)
const structure = AssemblySymmetry.getStructure(data, assemblySymmetry);
const { start, end } = rotation_axes[0]
const size = Vec3.distance(start, end)
const radius = (size / 500) * scale
const cage = getSymbolCage(symbol);
if (!cage) return Mesh.createEmpty(mesh);
const builderState = MeshBuilder.createState(256, 128, mesh)
builderState.currentGroup = 0
setSymbolTransform(t, symbol, rotation_axes, size, data)
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5)
Mat4.setTranslation(t, tmpCenter)
MeshBuilder.addCage(builderState, t, cage, radius, 1, 8)
const { start, end } = rotation_axes[0];
const size = Vec3.distance(start, end);
const radius = (size / 500) * scale;
return MeshBuilder.getMesh(builderState)
const builderState = MeshBuilder.createState(256, 128, mesh);
builderState.currentGroup = 0;
setSymbolTransform(t, symbol, rotation_axes, size, structure);
Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5);
Mat4.setTranslation(t, tmpCenter);
MeshBuilder.addCage(builderState, t, cage, radius, 1, 8);
return MeshBuilder.getMesh(builderState);
}
function getCageShape(ctx: RuntimeContext, data: Structure, props: AssemblySymmetryProps, shape?: Shape<Mesh>) {
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!;
const geo = getCageMesh(data, props, shape && shape.geometry);
const getColor = (groupId: number) => {
return props.cageColor
}
return props.cageColor;
};
const getLabel = (groupId: number) => {
const { type, symbol, kind } = assemblySymmetry
data.model.entryId
const { type, symbol, kind } = assemblySymmetry;
data.model.entryId;
return [
`<small>${data.model.entryId}</small>`,
`<small>${getAssemblyName(data)}</small>`,
`Cage of ${type} ${kind} (${symbol})`
].join(' | ')
}
return Shape.create('Cage', data, geo, getColor, () => 1, getLabel)
].join(' | ');
};
return Shape.create('Cage', data, geo, getColor, () => 1, getLabel);
}
//
export type AssemblySymmetryRepresentation = Representation<Structure, AssemblySymmetryParams>
export function AssemblySymmetryRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, AssemblySymmetryParams>): AssemblySymmetryRepresentation {
return Representation.createMulti('Assembly Symmetry', ctx, getParams, Representation.StateBuilder, AssemblySymmetryVisuals as unknown as Representation.Def<Structure, AssemblySymmetryParams>)
return Representation.createMulti('Assembly Symmetry', ctx, getParams, Representation.StateBuilder, AssemblySymmetryVisuals as unknown as Representation.Def<Structure, AssemblySymmetryParams>);
}

View File

@@ -5,13 +5,19 @@
*/
import * as React from 'react';
import { CollapsableState, CollapsableControls } from '../../../../../../mol-plugin-ui/base';
import { ApplyActionControl } from '../../../../../../mol-plugin-ui/state/apply-action';
import { InitAssemblySymmetry3D, AssemblySymmetry3D } from '../assembly-symmetry';
import { AssemblySymmetryProvider, AssemblySymmetryProps, AssemblySymmetryDataProvider } from '../../../../../../mol-model-props/rcsb/assembly-symmetry';
import { ParameterControls } from '../../../../../../mol-plugin-ui/controls/parameters';
import { ParamDefinition as PD } from '../../../../../../mol-util/param-definition';
import { StructureHierarchyManager } from '../../../../../../mol-plugin-state/manager/structure/hierarchy';
import { CollapsableState, CollapsableControls } from '../../../mol-plugin-ui/base';
import { ApplyActionControl } from '../../../mol-plugin-ui/state/apply-action';
import { InitAssemblySymmetry3D, AssemblySymmetry3D, AssemblySymmetryPreset, tryCreateAssemblySymmetry } from './behavior';
import { AssemblySymmetryProvider, AssemblySymmetryProps, AssemblySymmetryDataProvider, AssemblySymmetry } from './prop';
import { ParameterControls } from '../../../mol-plugin-ui/controls/parameters';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { StructureHierarchyManager } from '../../../mol-plugin-state/manager/structure/hierarchy';
import { StateAction, StateSelection } from '../../../mol-state';
import { PluginStateObject } from '../../../mol-plugin-state/objects';
import { PluginContext } from '../../../mol-plugin/context';
import { Task } from '../../../mol-task';
import Check from '@material-ui/icons/Check';
import Extension from '@material-ui/icons/Extension';
interface AssemblySymmetryControlState extends CollapsableState {
isBusy: boolean
@@ -24,7 +30,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
isCollapsed: false,
isBusy: false,
isHidden: true,
brand: { name: 'Sym', accent: 'cyan' }
brand: { accent: 'cyan', svg: Extension }
};
}
@@ -33,9 +39,9 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
this.setState({
isHidden: !this.canEnable(),
description: StructureHierarchyManager.getSelectedStructuresDescription(this.plugin)
})
});
});
this.subscribe(this.plugin.events.state.cell.stateUpdated, e => {
this.subscribe(this.plugin.state.events.cell.stateUpdated, e => {
if (e.cell.transform.transformer === AssemblySymmetry3D) this.forceUpdate();
});
this.subscribe(this.plugin.behaviors.state.isBusy, v => this.setState({ isBusy: v }));
@@ -56,7 +62,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
renderEnable() {
const pivot = this.pivot;
if (!pivot.cell.parent) return null;
return <ApplyActionControl state={pivot.cell.parent} action={InitAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: 'check' }} />;
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: Check }} />;
}
renderNoSymmetries() {
@@ -66,25 +72,25 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
}
get params() {
const structure = this.pivot.cell.obj?.data
const params = PD.clone(structure ? AssemblySymmetryProvider.getParams(structure) : AssemblySymmetryProvider.defaultParams)
params.serverUrl.isHidden = true
params.symmetryIndex.options = [[-1, 'Off'], ...params.symmetryIndex.options]
return params
const structure = this.pivot.cell.obj?.data;
const params = PD.clone(structure ? AssemblySymmetryProvider.getParams(structure) : AssemblySymmetryProvider.defaultParams);
params.serverUrl.isHidden = true;
params.symmetryIndex.options = [[-1, 'Off'], ...params.symmetryIndex.options];
return params;
}
get values() {
const structure = this.pivot.cell.obj?.data
const structure = this.pivot.cell.obj?.data;
if (structure) {
return AssemblySymmetryProvider.props(structure)
return AssemblySymmetryProvider.props(structure);
} else {
return { ...PD.getDefaultValues(AssemblySymmetryProvider.defaultParams), symmetryIndex: -1 }
return { ...PD.getDefaultValues(AssemblySymmetryProvider.defaultParams), symmetryIndex: -1 };
}
}
async updateAssemblySymmetry(values: AssemblySymmetryProps) {
const s = this.pivot
const currValues = AssemblySymmetryProvider.props(s.cell.obj!.data)
const s = this.pivot;
const currValues = AssemblySymmetryProvider.props(s.cell.obj!.data);
if (PD.areEqual(AssemblySymmetryProvider.defaultParams, currValues, values)) return;
if (s.properties) {
@@ -92,32 +98,43 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
b.to(s.properties.cell).update(old => {
old.properties[AssemblySymmetryProvider.descriptor.name] = values;
});
await this.plugin.updateDataState(b);
await b.commit();
} else {
const pd = this.plugin.customStructureProperties.getParams(s.cell.obj?.data);
const params = PD.getDefaultValues(pd);
params.properties[AssemblySymmetryProvider.descriptor.name] = values;
await this.plugin.builders.structure.insertStructureProperties(s.cell, params);
}
this.forceUpdate()
for (const components of this.plugin.managers.structure.hierarchy.currentComponentGroups) {
if (values.symmetryIndex === -1) {
const name = components[0]?.representations[0]?.cell.transform.params?.colorTheme.name;
if (name === AssemblySymmetry.Tag.Cluster) {
await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: 'default' });
}
} else {
tryCreateAssemblySymmetry(this.plugin, s.cell);
await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: AssemblySymmetry.Tag.Cluster as any });
}
}
}
paramsOnChange = (options: AssemblySymmetryProps) => {
this.updateAssemblySymmetry(options)
this.updateAssemblySymmetry(options);
}
get hasAssemblySymmetry3D() {
return !!this.pivot.genericRepresentations?.filter(r => r.cell.transform.transformer.id === AssemblySymmetry3D.id)[0]
return !this.pivot.cell.parent || !!StateSelection.findTagInSubtree(this.pivot.cell.parent.tree, this.pivot.cell.transform.ref, AssemblySymmetry.Tag.Representation);
}
get enable() {
return !this.hasAssemblySymmetry3D && this.values.symmetryIndex !== -1
return !this.hasAssemblySymmetry3D && this.values.symmetryIndex !== -1;
}
get noSymmetries() {
const structure = this.pivot.cell.obj?.data
const data = structure && AssemblySymmetryDataProvider.get(structure).value
return data && data.filter(sym => sym.symbol !== 'C1').length === 0
const structure = this.pivot.cell.obj?.data;
const data = structure && AssemblySymmetryDataProvider.get(structure).value;
return data && data.filter(sym => sym.symbol !== 'C1').length === 0;
}
renderParams() {
@@ -132,4 +149,10 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
if (this.enable) return this.renderEnable();
return this.renderParams();
}
}
}
const EnableAssemblySymmetry3D = StateAction.build({
from: PluginStateObject.Molecule.Structure,
})(({ a, ref, state }, plugin: PluginContext) => Task.create('Enable Assembly Symmetry', async ctx => {
await AssemblySymmetryPreset.apply(ref, Object.create(null), plugin);
}));

View File

@@ -1,7 +1,7 @@
schema: https://data-beta.rcsb.org/graphql
documents: './src/mol-model-props/rcsb/graphql/symmetry.gql.ts'
schema: https://data.rcsb.org/graphql
documents: './src/extensions/rcsb/graphql/symmetry.gql.ts'
generates:
'./src/mol-model-props/rcsb/graphql/types.ts':
'./src/extensions/rcsb/graphql/types.ts':
plugins:
- add: '/* eslint-disable */'
- time

View File

@@ -22,4 +22,4 @@ query AssemblySymmetry($assembly_id: String!, $entry_id: String!) {
}
}
}
`
`;

View File

@@ -1,7 +1,7 @@
/* eslint-disable */
export type Maybe<T> = T | null;
// Generated in 2020-03-30T11:30:30-07:00
// Generated in 2020-04-14T16:35:00-07:00
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
@@ -800,6 +800,8 @@ export type PdbxAuditRevisionCategory = {
export type PdbxAuditRevisionDetails = {
readonly data_content_type: Scalars['String'];
readonly description?: Maybe<Scalars['String']>;
readonly details?: Maybe<Scalars['String']>;
readonly ordinal: Scalars['Int'];
readonly provider?: Maybe<Scalars['String']>;
readonly revision_ordinal: Scalars['Int'];

View File

@@ -0,0 +1,8 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export { RCSBAssemblySymmetry } from './assembly-symmetry/behavior';
export { RCSBValidationReport } from './validation-report/behavior';

View File

@@ -4,23 +4,23 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../../../mol-util/param-definition'
import { PluginBehavior } from '../../../behavior';
import { ValidationReport, ValidationReportProvider } from '../../../../../mol-model-props/rcsb/validation-report';
import { RandomCoilIndexColorThemeProvider } from '../../../../../mol-model-props/rcsb/themes/random-coil-index';
import { GeometryQualityColorThemeProvider } from '../../../../../mol-model-props/rcsb/themes/geometry-quality';
import { Loci } from '../../../../../mol-model/loci';
import { OrderedSet } from '../../../../../mol-data/int';
import { ClashesRepresentationProvider } from '../../../../../mol-model-props/rcsb/representations/validation-report-clashes';
import { DensityFitColorThemeProvider } from '../../../../../mol-model-props/rcsb/themes/density-fit';
import { cantorPairing } from '../../../../../mol-data/util';
import { DefaultQueryRuntimeTable } from '../../../../../mol-script/runtime/query/compiler';
import { StructureSelectionQuery, StructureSelectionCategory } from '../../../../../mol-plugin-state/helpers/structure-selection-query';
import { MolScriptBuilder as MS } from '../../../../../mol-script/language/builder';
import { Task } from '../../../../../mol-task';
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../../../mol-plugin-state/builder/structure/representation-preset';
import { StateObjectRef } from '../../../../../mol-state';
import { Model } from '../../../../../mol-model/structure';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
import { ValidationReport, ValidationReportProvider } from './prop';
import { RandomCoilIndexColorThemeProvider } from './color/random-coil-index';
import { GeometryQualityColorThemeProvider } from './color/geometry-quality';
import { Loci } from '../../../mol-model/loci';
import { OrderedSet } from '../../../mol-data/int';
import { ClashesRepresentationProvider } from './representation';
import { DensityFitColorThemeProvider } from './color/density-fit';
import { cantorPairing } from '../../../mol-data/util';
import { DefaultQueryRuntimeTable } from '../../../mol-script/runtime/query/compiler';
import { StructureSelectionQuery, StructureSelectionCategory } from '../../../mol-plugin-state/helpers/structure-selection-query';
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
import { Task } from '../../../mol-task';
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
import { StateObjectRef } from '../../../mol-state';
import { Model } from '../../../mol-model/structure';
export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'rcsb-validation-report-prop',
@@ -34,12 +34,12 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
private labelProvider = {
label: (loci: Loci): string | undefined => {
if (!this.params.showTooltip) return
if (!this.params.showTooltip) return;
return [
geometryQualityLabel(loci),
densityFitLabel(loci),
randomCoilIndexLabel(loci)
].filter(l => !!l).join('</br>')
].filter(l => !!l).join('</br>');
}
}
@@ -50,20 +50,20 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
this.ctx.managers.lociLabels.addProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(DensityFitColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.add(GeometryQualityColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.add(RandomCoilIndexColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.add(DensityFitColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(GeometryQualityColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(RandomCoilIndexColorThemeProvider);
this.ctx.representation.structure.registry.add(ClashesRepresentationProvider)
this.ctx.query.structure.registry.add(hasClash)
this.ctx.representation.structure.registry.add(ClashesRepresentationProvider);
this.ctx.query.structure.registry.add(hasClash);
this.ctx.builders.structure.representation.registerPreset(ValidationReportGeometryQualityPreset)
this.ctx.builders.structure.representation.registerPreset(ValidationReportDensityFitPreset)
this.ctx.builders.structure.representation.registerPreset(ValidationReportRandomCoilIndexPreset)
this.ctx.builders.structure.representation.registerPreset(ValidationReportGeometryQualityPreset);
this.ctx.builders.structure.representation.registerPreset(ValidationReportDensityFitPreset);
this.ctx.builders.structure.representation.registerPreset(ValidationReportRandomCoilIndexPreset);
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach
let updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.params.showTooltip = p.showTooltip;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
@@ -78,16 +78,16 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
this.ctx.managers.lociLabels.removeProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(DensityFitColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.remove(GeometryQualityColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.remove(RandomCoilIndexColorThemeProvider)
this.ctx.representation.structure.themes.colorThemeRegistry.remove(DensityFitColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(GeometryQualityColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(RandomCoilIndexColorThemeProvider);
this.ctx.representation.structure.registry.remove(ClashesRepresentationProvider)
this.ctx.query.structure.registry.remove(hasClash)
this.ctx.representation.structure.registry.remove(ClashesRepresentationProvider);
this.ctx.query.structure.registry.remove(hasClash);
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportGeometryQualityPreset)
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportDensityFitPreset)
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportRandomCoilIndexPreset)
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportGeometryQualityPreset);
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportDensityFitPreset);
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportRandomCoilIndexPreset);
}
},
params: () => ({
@@ -101,78 +101,78 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
function geometryQualityLabel(loci: Loci): string | undefined {
if (loci.kind === 'element-loci') {
if (loci.elements.length === 0) return
if (loci.elements.length === 0) return;
if (loci.elements.length === 1 && OrderedSet.size(loci.elements[0].indices) === 1) {
const { unit, indices } = loci.elements[0]
const { unit, indices } = loci.elements[0];
const validationReport = ValidationReportProvider.get(unit.model).value
if (!validationReport) return
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) return
const validationReport = ValidationReportProvider.get(unit.model).value;
if (!validationReport) return;
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) return;
const { bondOutliers, angleOutliers } = validationReport
const eI = unit.elements[OrderedSet.start(indices)]
const issues = new Set<string>()
const { bondOutliers, angleOutliers } = validationReport;
const eI = unit.elements[OrderedSet.start(indices)];
const issues = new Set<string>();
const bonds = bondOutliers.index.get(eI)
if (bonds) bonds.forEach(b => issues.add(bondOutliers.data[b].tag))
const bonds = bondOutliers.index.get(eI);
if (bonds) bonds.forEach(b => issues.add(bondOutliers.data[b].tag));
const angles = angleOutliers.index.get(eI)
if (angles) angles.forEach(a => issues.add(angleOutliers.data[a].tag))
const angles = angleOutliers.index.get(eI);
if (angles) angles.forEach(a => issues.add(angleOutliers.data[a].tag));
if (issues.size === 0) {
return `Geometry Quality <small>(1 Atom)</small>: no issues`;
}
const summary: string[] = []
issues.forEach(name => summary.push(name))
const summary: string[] = [];
issues.forEach(name => summary.push(name));
return `Geometry Quality <small>(1 Atom)</small>: ${summary.join(', ')}`;
}
let hasValidationReport = false
const seen = new Set<number>()
const cummulativeIssues = new Map<string, number>()
let hasValidationReport = false;
const seen = new Set<number>();
const cummulativeIssues = new Map<string, number>();
for (const { indices, unit } of loci.elements) {
const validationReport = ValidationReportProvider.get(unit.model).value
if (!validationReport) continue
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
hasValidationReport = true
const validationReport = ValidationReportProvider.get(unit.model).value;
if (!validationReport) continue;
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue;
hasValidationReport = true;
const { geometryIssues } = validationReport
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index
const { elements } = unit
const { geometryIssues } = validationReport;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const { elements } = unit;
OrderedSet.forEach(indices, idx => {
const eI = elements[idx]
const eI = elements[idx];
const rI = residueIndex[eI]
const residueKey = cantorPairing(rI, unit.id)
const rI = residueIndex[eI];
const residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const issues = geometryIssues.get(rI)
const issues = geometryIssues.get(rI);
if (issues) {
issues.forEach(name => {
const count = cummulativeIssues.get(name) || 0
cummulativeIssues.set(name, count + 1)
})
const count = cummulativeIssues.get(name) || 0;
cummulativeIssues.set(name, count + 1);
});
}
seen.add(residueKey)
seen.add(residueKey);
}
})
});
}
if (!hasValidationReport) return
if (!hasValidationReport) return;
const residueCount = `<small>(${seen.size} ${seen.size > 1 ? 'Residues' : 'Residue'})</small>`
const residueCount = `<small>(${seen.size} ${seen.size > 1 ? 'Residues' : 'Residue'})</small>`;
if (cummulativeIssues.size === 0) {
return `Geometry Quality ${residueCount}: no issues`;
}
const summary: string[] = []
const summary: string[] = [];
cummulativeIssues.forEach((count, name) => {
summary.push(`${name}${count > 1 ? ` \u00D7 ${count}` : ''}`)
})
summary.push(`${name}${count > 1 ? ` \u00D7 ${count}` : ''}`);
});
return `Geometry Quality ${residueCount}: ${summary.join(', ')}`;
}
}
@@ -181,58 +181,58 @@ function densityFitLabel(loci: Loci): string | undefined {
if (loci.kind === 'element-loci') {
if (loci.elements.length === 0) return;
const seen = new Set<number>()
const rsrzSeen = new Set<number>()
const rsccSeen = new Set<number>()
let rsrzSum = 0
let rsccSum = 0
const seen = new Set<number>();
const rsrzSeen = new Set<number>();
const rsccSeen = new Set<number>();
let rsrzSum = 0;
let rsccSum = 0;
for (const { indices, unit } of loci.elements) {
const validationReport = ValidationReportProvider.get(unit.model).value
if (!validationReport) continue
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
const validationReport = ValidationReportProvider.get(unit.model).value;
if (!validationReport) continue;
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue;
const { rsrz, rscc } = validationReport
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index
const { elements } = unit
const { rsrz, rscc } = validationReport;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const { elements } = unit;
OrderedSet.forEach(indices, idx => {
const eI = elements[idx]
const rI = residueIndex[eI]
const eI = elements[idx];
const rI = residueIndex[eI];
const residueKey = cantorPairing(rI, unit.id)
const residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const rsrzValue = rsrz.get(rI)
const rsccValue = rscc.get(rI)
const rsrzValue = rsrz.get(rI);
const rsccValue = rscc.get(rI);
if (rsrzValue !== undefined) {
rsrzSum += rsrzValue
rsrzSeen.add(residueKey)
rsrzSum += rsrzValue;
rsrzSeen.add(residueKey);
} else if (rsccValue !== undefined) {
rsccSum += rsccValue
rsccSeen.add(residueKey)
rsccSum += rsccValue;
rsccSeen.add(residueKey);
}
seen.add(residueKey)
seen.add(residueKey);
}
})
});
}
if (seen.size === 0) return
if (seen.size === 0) return;
const summary: string[] = []
const summary: string[] = [];
if (rsrzSeen.size) {
const rsrzCount = `<small>(${rsrzSeen.size} ${rsrzSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`
const rsrzAvg = rsrzSum / rsrzSeen.size
summary.push(`Real Space R ${rsrzCount}: ${rsrzAvg.toFixed(2)}`)
const rsrzCount = `<small>(${rsrzSeen.size} ${rsrzSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
const rsrzAvg = rsrzSum / rsrzSeen.size;
summary.push(`Real Space R ${rsrzCount}: ${rsrzAvg.toFixed(2)}`);
}
if (rsccSeen.size) {
const rsccCount = `<small>(${rsccSeen.size} ${rsccSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`
const rsccAvg = rsccSum / rsccSeen.size
summary.push(`Real Space Correlation Coefficient ${rsccCount}: ${rsccAvg.toFixed(2)}`)
const rsccCount = `<small>(${rsccSeen.size} ${rsccSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
const rsccAvg = rsccSum / rsccSeen.size;
summary.push(`Real Space Correlation Coefficient ${rsccCount}: ${rsccAvg.toFixed(2)}`);
}
if (summary.length) {
return summary.join('</br>')
return summary.join('</br>');
}
}
}
@@ -241,39 +241,39 @@ function randomCoilIndexLabel(loci: Loci): string | undefined {
if (loci.kind === 'element-loci') {
if (loci.elements.length === 0) return;
const seen = new Set<number>()
let sum = 0
const seen = new Set<number>();
let sum = 0;
for (const { indices, unit } of loci.elements) {
const validationReport = ValidationReportProvider.get(unit.model).value
if (!validationReport) continue
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
const validationReport = ValidationReportProvider.get(unit.model).value;
if (!validationReport) continue;
if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue;
const { rci } = validationReport
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index
const { elements } = unit
const { rci } = validationReport;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const { elements } = unit;
OrderedSet.forEach(indices, idx => {
const eI = elements[idx]
const rI = residueIndex[eI]
const eI = elements[idx];
const rI = residueIndex[eI];
const residueKey = cantorPairing(rI, unit.id)
const residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const rciValue = rci.get(rI)
const rciValue = rci.get(rI);
if (rciValue !== undefined) {
sum += rciValue
seen.add(residueKey)
sum += rciValue;
seen.add(residueKey);
}
}
})
});
}
if (seen.size === 0) return
if (seen.size === 0) return;
const residueCount = `<small>(${seen.size} ${seen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`
const rciAvg = sum / seen.size
const residueCount = `<small>(${seen.size} ${seen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
const rciAvg = sum / seen.size;
return `Random Coil Index ${residueCount}: ${rciAvg.toFixed(2)}`
return `Random Coil Index ${residueCount}: ${rciAvg.toFixed(2)}`;
}
}
@@ -292,9 +292,9 @@ const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modi
description: 'Select residues with clashes in the wwPDB validation report.',
category: StructureSelectionCategory.Residue,
ensureCustomProperties: (ctx, structure) => {
return ValidationReportProvider.attach(ctx, structure.models[0])
return ValidationReportProvider.attach(ctx, structure.models[0]);
}
})
});
//
@@ -305,22 +305,22 @@ export const ValidationReportGeometryQualityPreset = StructureRepresentationPres
description: 'Color structure based on geometry quality; show geometry clashes. Data from wwPDB Validation Report, obtained via RCSB PDB.'
},
isApplicable(a) {
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0])
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const model = structureCell?.obj?.data.model
const model = structureCell?.obj?.data.model;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
}))
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
}));
const colorTheme = GeometryQualityColorThemeProvider.name as any
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
const colorTheme = GeometryQualityColorThemeProvider.name as any;
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' })
const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' });
const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
let clashesBallAndStick, clashesSnfg3d;
@@ -329,7 +329,7 @@ export const ValidationReportGeometryQualityPreset = StructureRepresentationPres
clashesSnfg3d = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-snfg-3d' });
}
await plugin.updateDataState(update, { revertOnError: false });
await update.commit({ revertOnError: true });
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } };
}
});
@@ -341,20 +341,20 @@ export const ValidationReportDensityFitPreset = StructureRepresentationPresetPro
description: 'Color structure based on density fit. Data from wwPDB Validation Report, obtained via RCSB PDB.'
},
isApplicable(a) {
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0])
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const model = structureCell?.obj?.data.model
const model = structureCell?.obj?.data.model;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
}))
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
}));
const colorTheme = DensityFitColorThemeProvider.name as any
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
const colorTheme = DensityFitColorThemeProvider.name as any;
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
}
});
@@ -365,19 +365,19 @@ export const ValidationReportRandomCoilIndexPreset = StructureRepresentationPres
description: 'Color structure based on Random Coil Index. Data from wwPDB Validation Report, obtained via RCSB PDB.'
},
isApplicable(a) {
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromNmr(a.data.models[0])
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromNmr(a.data.models[0]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const model = structureCell?.obj?.data.model
const model = structureCell?.obj?.data.model;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
}))
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
}));
const colorTheme = RandomCoilIndexColorThemeProvider.name as any
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
const colorTheme = RandomCoilIndexColorThemeProvider.name as any;
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
}
});

View File

@@ -4,49 +4,49 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { Color, ColorScale } from '../../../mol-util/color';
import { StructureElement } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { CustomProperty } from '../../common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../validation-report';
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Color, ColorScale } from '../../../../mol-util/color';
import { StructureElement, Model } from '../../../../mol-model/structure';
import { Location } from '../../../../mol-model/location';
import { CustomProperty } from '../../../../mol-model-props/common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../prop';
const DefaultColor = Color(0xCCCCCC)
const DefaultColor = Color(0xCCCCCC);
export function DensityFitColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
let color: LocationColor = () => DefaultColor
let color: LocationColor = () => DefaultColor;
const scaleRsrz = ColorScale.create({
minLabel: 'Poor',
maxLabel: 'Better',
domain: [2, 0],
listOrName: 'red-yellow-blue',
})
});
const scaleRscc = ColorScale.create({
minLabel: 'Poor',
maxLabel: 'Better',
domain: [0.678, 1.0],
listOrName: 'red-yellow-blue',
})
});
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0])
const contextHash = validationReport?.version
const model = ctx.structure?.models[0]
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]);
const contextHash = validationReport?.version;
const model = ctx.structure?.models[0];
if (validationReport?.value && model) {
const { rsrz, rscc } = validationReport.value
const residueIndex = model.atomicHierarchy.residueAtomSegments.index
const { rsrz, rscc } = validationReport.value;
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
color = (location: Location): Color => {
if (StructureElement.Location.is(location) && location.unit.model === model) {
const rsrzValue = rsrz.get(residueIndex[location.element])
if (rsrzValue !== undefined) return scaleRsrz.color(rsrzValue)
const rsccValue = rscc.get(residueIndex[location.element])
if (rsccValue !== undefined) return scaleRscc.color(rsccValue)
return DefaultColor
const rsrzValue = rsrz.get(residueIndex[location.element]);
if (rsrzValue !== undefined) return scaleRsrz.color(rsrzValue);
const rsccValue = rscc.get(residueIndex[location.element]);
if (rsccValue !== undefined) return scaleRscc.color(rsccValue);
return DefaultColor;
}
return DefaultColor
}
return DefaultColor;
};
}
return {
@@ -57,7 +57,7 @@ export function DensityFitColorTheme(ctx: ThemeDataContext, props: {}): ColorThe
contextHash,
description: 'Assigns residue colors according to the density fit using normalized Real Space R (RSRZ) for polymer residues and real space correlation coefficient (RSCC) for ligands. Colors range from poor (RSRZ = 2 or RSCC = 0.678) - to better (RSRZ = 0 or RSCC = 1.0). Data from wwPDB Validation Report, obtained via RCSB PDB.',
legend: scaleRsrz.legend
}
};
}
export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationReport.Tag.DensityFit> = {
@@ -67,9 +67,9 @@ export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationRep
factory: DensityFitColorTheme,
getParams: () => ({}),
defaultValues: PD.getDefaultValues({}),
isApplicable: (ctx: ThemeDataContext) => ValidationReport.isApplicable(ctx.structure?.models[0]),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.hasXrayMap(ctx.structure.models[0]),
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)
}
}
};

View File

@@ -4,24 +4,24 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { Color } from '../../../mol-util/color';
import { StructureElement } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { CustomProperty } from '../../common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../validation-report';
import { TableLegend } from '../../../mol-util/legend';
import { PolymerType } from '../../../mol-model/structure/model/types';
import { SetUtils } from '../../../mol-util/set';
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Color } from '../../../../mol-util/color';
import { StructureElement } from '../../../../mol-model/structure';
import { Location } from '../../../../mol-model/location';
import { CustomProperty } from '../../../../mol-model-props/common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../prop';
import { TableLegend } from '../../../../mol-util/legend';
import { PolymerType } from '../../../../mol-model/structure/model/types';
import { SetUtils } from '../../../../mol-util/set';
const DefaultColor = Color(0x909090)
const DefaultColor = Color(0x909090);
const NoIssuesColor = Color(0x2166ac)
const OneIssueColor = Color(0xfee08b)
const TwoIssuesColor = Color(0xf46d43)
const ThreeOrMoreIssuesColor = Color(0xa50026)
const NoIssuesColor = Color(0x2166ac);
const OneIssueColor = Color(0xfee08b);
const TwoIssuesColor = Color(0xf46d43);
const ThreeOrMoreIssuesColor = Color(0xa50026);
const ColorLegend = TableLegend([
['Data unavailable', DefaultColor],
@@ -29,64 +29,64 @@ const ColorLegend = TableLegend([
['One issue', OneIssueColor],
['Two issues', TwoIssuesColor],
['Three or more issues', ThreeOrMoreIssuesColor],
])
]);
export function getGeometricQualityColorThemeParams(ctx: ThemeDataContext) {
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]).value
const options: [string, string][] = []
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]).value;
const options: [string, string][] = [];
if (validationReport) {
const kinds = new Set<string>()
validationReport.geometryIssues.forEach(v => v.forEach(k => kinds.add(k)))
kinds.forEach(k => options.push([k, k]))
const kinds = new Set<string>();
validationReport.geometryIssues.forEach(v => v.forEach(k => kinds.add(k)));
kinds.forEach(k => options.push([k, k]));
}
return {
ignore: PD.MultiSelect([] as string[], options)
}
};
}
export type GeometricQualityColorThemeParams = ReturnType<typeof getGeometricQualityColorThemeParams>
export function GeometryQualityColorTheme(ctx: ThemeDataContext, props: PD.Values<GeometricQualityColorThemeParams>): ColorTheme<GeometricQualityColorThemeParams> {
let color: LocationColor = () => DefaultColor
let color: LocationColor = () => DefaultColor;
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0])
const contextHash = validationReport?.version
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]);
const contextHash = validationReport?.version;
const value = validationReport?.value
const model = ctx.structure?.models[0]
const value = validationReport?.value;
const model = ctx.structure?.models[0];
if (value && model) {
const { geometryIssues, clashes, bondOutliers, angleOutliers } = value
const residueIndex = model.atomicHierarchy.residueAtomSegments.index
const { polymerType } = model.atomicHierarchy.derived.residue
const ignore = new Set(props.ignore)
const { geometryIssues, clashes, bondOutliers, angleOutliers } = value;
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
const { polymerType } = model.atomicHierarchy.derived.residue;
const ignore = new Set(props.ignore);
color = (location: Location): Color => {
if (StructureElement.Location.is(location) && location.unit.model === model) {
const { element } = location
const rI = residueIndex[element]
const { element } = location;
const rI = residueIndex[element];
const value = geometryIssues.get(rI)
if (value === undefined) return DefaultColor
const value = geometryIssues.get(rI);
if (value === undefined) return DefaultColor;
let count = SetUtils.differenceSize(value, ignore)
let count = SetUtils.differenceSize(value, ignore);
if (count > 0 && polymerType[rI] === PolymerType.NA) {
count = 0
if (!ignore.has('clash') && clashes.getVertexEdgeCount(element) > 0) count += 1
if (!ignore.has('mog-bond-outlier') && bondOutliers.index.has(element)) count += 1
if (!ignore.has('mog-angle-outlier') && angleOutliers.index.has(element)) count += 1
count = 0;
if (!ignore.has('clash') && clashes.getVertexEdgeCount(element) > 0) count += 1;
if (!ignore.has('mog-bond-outlier') && bondOutliers.index.has(element)) count += 1;
if (!ignore.has('mog-angle-outlier') && angleOutliers.index.has(element)) count += 1;
}
switch (count) {
case undefined: return DefaultColor
case 0: return NoIssuesColor
case 1: return OneIssueColor
case 2: return TwoIssuesColor
default: return ThreeOrMoreIssuesColor
case undefined: return DefaultColor;
case 0: return NoIssuesColor;
case 1: return OneIssueColor;
case 2: return TwoIssuesColor;
default: return ThreeOrMoreIssuesColor;
}
}
return DefaultColor
}
return DefaultColor;
};
}
return {
@@ -97,7 +97,7 @@ export function GeometryQualityColorTheme(ctx: ThemeDataContext, props: PD.Value
contextHash,
description: 'Assigns residue colors according to the number of (filtered) geometry issues. Data from wwPDB Validation Report, obtained via RCSB PDB.',
legend: ColorLegend
}
};
}
export const GeometryQualityColorThemeProvider: ColorTheme.Provider<GeometricQualityColorThemeParams, ValidationReport.Tag.GeometryQuality> = {
@@ -112,4 +112,4 @@ export const GeometryQualityColorThemeProvider: ColorTheme.Provider<GeometricQua
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)
}
}
};

View File

@@ -4,40 +4,40 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { Color, ColorScale } from '../../../mol-util/color';
import { StructureElement } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { CustomProperty } from '../../common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../validation-report';
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { ColorTheme, LocationColor } from '../../../../mol-theme/color';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Color, ColorScale } from '../../../../mol-util/color';
import { StructureElement, Model } from '../../../../mol-model/structure';
import { Location } from '../../../../mol-model/location';
import { CustomProperty } from '../../../../mol-model-props/common/custom-property';
import { ValidationReportProvider, ValidationReport } from '../prop';
const DefaultColor = Color(0xCCCCCC)
const DefaultColor = Color(0xCCCCCC);
export function RandomCoilIndexColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
let color: LocationColor = () => DefaultColor
let color: LocationColor = () => DefaultColor;
const scale = ColorScale.create({
reverse: true,
domain: [0, 0.6],
listOrName: 'red-yellow-blue',
})
});
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0])
const contextHash = validationReport?.version
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]);
const contextHash = validationReport?.version;
const rci = validationReport?.value?.rci
const model = ctx.structure?.models[0]
const rci = validationReport?.value?.rci;
const model = ctx.structure?.models[0];
if (rci && model) {
const residueIndex = model.atomicHierarchy.residueAtomSegments.index
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
color = (location: Location): Color => {
if (StructureElement.Location.is(location) && location.unit.model === model) {
const value = rci.get(residueIndex[location.element])
return value === undefined ? DefaultColor : scale.color(value)
const value = rci.get(residueIndex[location.element]);
return value === undefined ? DefaultColor : scale.color(value);
}
return DefaultColor
}
return DefaultColor;
};
}
return {
@@ -48,7 +48,7 @@ export function RandomCoilIndexColorTheme(ctx: ThemeDataContext, props: {}): Col
contextHash,
description: 'Assigns residue colors according to the Random Coil Index value. Data from wwPDB Validation Report, obtained via RCSB PDB.',
legend: scale.legend
}
};
}
export const RandomCoilIndexColorThemeProvider: ColorTheme.Provider<{}, ValidationReport.Tag.RandomCoilIndex> = {
@@ -58,9 +58,9 @@ export const RandomCoilIndexColorThemeProvider: ColorTheme.Provider<{}, Validati
factory: RandomCoilIndexColorTheme,
getParams: () => ({}),
defaultValues: PD.getDefaultValues({}),
isApplicable: (ctx: ThemeDataContext) => ValidationReport.isApplicable(ctx.structure?.models[0]),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.isFromNmr(ctx.structure.models[0]),
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)
}
}
};

View File

@@ -4,26 +4,25 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../mol-util/param-definition'
import { CustomPropertyDescriptor, Structure, Unit } from '../../mol-model/structure';
import { CustomProperty } from '../common/custom-property';
import { CustomModelProperty } from '../common/custom-model-property';
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure/model';
import { IntAdjacencyGraph } from '../../mol-math/graph';
import { readFromFile } from '../../mol-util/data-source';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { InterUnitGraph } from '../../mol-math/graph/inter-unit-graph';
import { UnitIndex } from '../../mol-model/structure/structure/element/element';
import { IntMap, SortedArray } from '../../mol-data/int';
import { arrayMax } from '../../mol-util/array';
import { equalEps } from '../../mol-math/linear-algebra/3d/common';
import { Vec3 } from '../../mol-math/linear-algebra';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
import { CustomPropSymbol } from '../../mol-script/language/symbol';
import Type from '../../mol-script/language/type';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { CustomPropertyDescriptor, Structure, Unit } from '../../../mol-model/structure';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
import { Model, ElementIndex, ResidueIndex } from '../../../mol-model/structure/model';
import { IntAdjacencyGraph } from '../../../mol-math/graph';
import { CustomStructureProperty } from '../../../mol-model-props/common/custom-structure-property';
import { InterUnitGraph } from '../../../mol-math/graph/inter-unit-graph';
import { UnitIndex } from '../../../mol-model/structure/structure/element/element';
import { IntMap, SortedArray } from '../../../mol-data/int';
import { arrayMax } from '../../../mol-util/array';
import { equalEps } from '../../../mol-math/linear-algebra/3d/common';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
import Type from '../../../mol-script/language/type';
import { Asset } from '../../../mol-util/assets';
export { ValidationReport }
export { ValidationReport };
interface ValidationReport {
/**
@@ -85,71 +84,67 @@ namespace ValidationReport {
Clashes = 'rcsb-clashes',
}
export const DefaultBaseUrl = '//ftp.rcsb.org/pub/pdb/validation_reports'
export const DefaultBaseUrl = '//ftp.rcsb.org/pub/pdb/validation_reports';
export function getEntryUrl(pdbId: string, baseUrl: string) {
const id = pdbId.toLowerCase()
return `${baseUrl}/${id.substr(1, 2)}/${id}/${id}_validation.xml.gz`
const id = pdbId.toLowerCase();
return `${baseUrl}/${id.substr(1, 2)}/${id}/${id}_validation.xml.gz`;
}
export function isApplicable(model?: Model): boolean {
return (
!!model &&
MmcifFormat.is(model.sourceData) &&
(model.sourceData.data.db.database_2.database_id.isDefined ||
model.entryId.length === 4)
)
return !!model && Model.isFromPdbArchive(model);
}
export function fromXml(xml: XMLDocument, model: Model): ValidationReport {
return parseValidationReportXml(xml, model)
return parseValidationReportXml(xml, model);
}
export async function fetch(ctx: CustomProperty.Context, model: Model, props: ServerSourceProps): Promise<ValidationReport> {
const url = getEntryUrl(model.entryId, props.baseUrl)
const xml = await ctx.fetch({ url, type: 'xml' }).runInContext(ctx.runtime)
return fromXml(xml, model)
export async function fetch(ctx: CustomProperty.Context, model: Model, props: ServerSourceProps): Promise<CustomProperty.Data<ValidationReport>> {
const url = Asset.getUrlAsset(ctx.assetManager, getEntryUrl(model.entryId, props.baseUrl));
const xml = await ctx.assetManager.resolve(url, 'xml').runInContext(ctx.runtime);
return { value: fromXml(xml.data, model), assets: [xml] };
}
export async function open(ctx: CustomProperty.Context, model: Model, props: FileSourceProps): Promise<ValidationReport> {
const xml = await readFromFile(props.input, 'xml').runInContext(ctx.runtime)
return fromXml(xml, model)
export async function open(ctx: CustomProperty.Context, model: Model, props: FileSourceProps): Promise<CustomProperty.Data<ValidationReport>> {
if (props.input === null) throw new Error('No file given');
const xml = await ctx.assetManager.resolve(props.input, 'xml').runInContext(ctx.runtime);
return { value: fromXml(xml.data, model), assets: [xml] };
}
export async function obtain(ctx: CustomProperty.Context, model: Model, props: ValidationReportProps): Promise<ValidationReport> {
export async function obtain(ctx: CustomProperty.Context, model: Model, props: ValidationReportProps): Promise<CustomProperty.Data<ValidationReport>> {
switch(props.source.name) {
case 'file': return open(ctx, model, props.source.params)
case 'server': return fetch(ctx, model, props.source.params)
case 'file': return open(ctx, model, props.source.params);
case 'server': return fetch(ctx, model, props.source.params);
}
}
export const symbols = {
hasClash: QuerySymbolRuntime.Dynamic(CustomPropSymbol('rcsb', 'validation-report.has-clash', Type.Bool),
ctx => {
const { unit, element } = ctx.element
if (!Unit.isAtomic(unit)) return 0
const validationReport = ValidationReportProvider.get(unit.model).value
return validationReport && validationReport.clashes.getVertexEdgeCount(element) > 0
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit)) return 0;
const validationReport = ValidationReportProvider.get(unit.model).value;
return validationReport && validationReport.clashes.getVertexEdgeCount(element) > 0;
}
),
issueCount: QuerySymbolRuntime.Dynamic(CustomPropSymbol('rcsb', 'validation-report.issue-count', Type.Num),
ctx => {
const { unit, element } = ctx.element
if (!Unit.isAtomic(unit)) return 0
const validationReport = ValidationReportProvider.get(unit.model).value
return validationReport?.geometryIssues.get(unit.residueIndex[element])?.size || 0
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit)) return 0;
const validationReport = ValidationReportProvider.get(unit.model).value;
return validationReport?.geometryIssues.get(unit.residueIndex[element])?.size || 0;
}
),
}
};
}
const FileSourceParams = {
input: PD.File({ accept: '.xml,.gz,.zip' })
}
};
type FileSourceProps = PD.Values<typeof FileSourceParams>
const ServerSourceParams = {
baseUrl: PD.Text(ValidationReport.DefaultBaseUrl, { description: 'Base URL to directory tree' })
}
};
type ServerSourceProps = PD.Values<typeof ServerSourceParams>
export const ValidationReportParams = {
@@ -157,7 +152,7 @@ export const ValidationReportParams = {
'file': PD.Group(FileSourceParams, { label: 'File', isFlat: true }),
'server': PD.Group(ServerSourceParams, { label: 'Server', isFlat: true }),
}, { options: [['file', 'File'], ['server', 'Server']] })
}
};
export type ValidationReportParams = typeof ValidationReportParams
export type ValidationReportProps = PD.Values<ValidationReportParams>
@@ -172,10 +167,10 @@ export const ValidationReportProvider: CustomModelProperty.Provider<ValidationRe
getParams: (data: Model) => ValidationReportParams,
isApplicable: (data: Model) => ValidationReport.isApplicable(data),
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<ValidationReportProps>) => {
const p = { ...PD.getDefaultValues(ValidationReportParams), ...props }
return await ValidationReport.obtain(ctx, data, p)
const p = { ...PD.getDefaultValues(ValidationReportParams), ...props };
return await ValidationReport.obtain(ctx, data, p);
}
})
});
//
@@ -199,25 +194,25 @@ export interface Clashes {
}
function createInterUnitClashes(structure: Structure, clashes: ValidationReport['clashes']) {
const builder = new InterUnitGraph.Builder<Unit.Atomic, UnitIndex, InterUnitClashesProps>()
const { a, b, edgeProps: { id, magnitude, distance } } = clashes
const builder = new InterUnitGraph.Builder<Unit.Atomic, UnitIndex, InterUnitClashesProps>();
const { a, b, edgeProps: { id, magnitude, distance } } = clashes;
const pA = Vec3(), pB = Vec3()
const pA = Vec3(), pB = Vec3();
Structure.eachUnitPair(structure, (unitA: Unit, unitB: Unit) => {
const elementsA = unitA.elements
const elementsB = unitB.elements
const elementsA = unitA.elements;
const elementsB = unitB.elements;
builder.startUnitPair(unitA as Unit.Atomic, unitB as Unit.Atomic)
builder.startUnitPair(unitA as Unit.Atomic, unitB as Unit.Atomic);
for (let i = 0, il = clashes.edgeCount * 2; i < il; ++i) {
// TODO create lookup
let indexA = SortedArray.indexOf(elementsA, a[i])
let indexB = SortedArray.indexOf(elementsB, b[i])
let indexA = SortedArray.indexOf(elementsA, a[i]);
let indexB = SortedArray.indexOf(elementsB, b[i]);
if (indexA !== -1 && indexB !== -1) {
unitA.conformation.position(a[i], pA)
unitB.conformation.position(b[i], pB)
unitA.conformation.position(a[i], pA);
unitB.conformation.position(b[i], pB);
// check actual distance to avoid clashes between unrelated chain instances
if (equalEps(distance[i], Vec3.distance(pA, pB), 0.1)) {
@@ -225,83 +220,83 @@ function createInterUnitClashes(structure: Structure, clashes: ValidationReport[
id: id[i],
magnitude: magnitude[i],
distance: distance[i],
})
});
}
}
}
builder.finishUnitPair()
builder.finishUnitPair();
}, {
maxRadius: arrayMax(clashes.edgeProps.distance),
validUnit: (unit: Unit) => Unit.isAtomic(unit),
validUnitPair: (unitA: Unit, unitB: Unit) => unitA.model === unitB.model
})
});
return new InterUnitGraph(builder.getMap())
return new InterUnitGraph(builder.getMap());
}
function createIntraUnitClashes(unit: Unit.Atomic, clashes: ValidationReport['clashes']): IntraUnitClashes {
const aIndices: UnitIndex[] = []
const bIndices: UnitIndex[] = []
const ids: number[] = []
const magnitudes: number[] = []
const distances: number[] = []
const aIndices: UnitIndex[] = [];
const bIndices: UnitIndex[] = [];
const ids: number[] = [];
const magnitudes: number[] = [];
const distances: number[] = [];
const pA = Vec3(), pB = Vec3()
const pA = Vec3(), pB = Vec3();
const { elements } = unit
const { a, b, edgeCount, edgeProps } = clashes
const { elements } = unit;
const { a, b, edgeCount, edgeProps } = clashes;
for (let i = 0, il = edgeCount * 2; i < il; ++i) {
// TODO create lookup
let indexA = SortedArray.indexOf(elements, a[i])
let indexB = SortedArray.indexOf(elements, b[i])
let indexA = SortedArray.indexOf(elements, a[i]);
let indexB = SortedArray.indexOf(elements, b[i]);
if (indexA !== -1 && indexB !== -1) {
unit.conformation.position(a[i], pA)
unit.conformation.position(b[i], pB)
unit.conformation.position(a[i], pA);
unit.conformation.position(b[i], pB);
// check actual distance to avoid clashes between unrelated chain instances
if (equalEps(edgeProps.distance[i], Vec3.distance(pA, pB), 0.1)) {
aIndices.push(indexA as UnitIndex)
bIndices.push(indexB as UnitIndex)
ids.push(edgeProps.id[i])
magnitudes.push(edgeProps.magnitude[i])
distances.push(edgeProps.distance[i])
aIndices.push(indexA as UnitIndex);
bIndices.push(indexB as UnitIndex);
ids.push(edgeProps.id[i]);
magnitudes.push(edgeProps.magnitude[i]);
distances.push(edgeProps.distance[i]);
}
}
}
const builder = new IntAdjacencyGraph.EdgeBuilder(elements.length, aIndices, bIndices)
const id = new Int32Array(builder.slotCount)
const magnitude = new Float32Array(builder.slotCount)
const distance = new Float32Array(builder.slotCount)
const builder = new IntAdjacencyGraph.EdgeBuilder(elements.length, aIndices, bIndices);
const id = new Int32Array(builder.slotCount);
const magnitude = new Float32Array(builder.slotCount);
const distance = new Float32Array(builder.slotCount);
for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge()
builder.assignProperty(id, ids[i])
builder.assignProperty(magnitude, magnitudes[i])
builder.assignProperty(distance, distances[i])
builder.addNextEdge();
builder.assignProperty(id, ids[i]);
builder.assignProperty(magnitude, magnitudes[i]);
builder.assignProperty(distance, distances[i]);
}
return builder.createGraph({ id, magnitude, distance })
return builder.createGraph({ id, magnitude, distance });
}
function createClashes(structure: Structure, clashes: ValidationReport['clashes']): Clashes {
const intraUnit = IntMap.Mutable<IntraUnitClashes>()
const intraUnit = IntMap.Mutable<IntraUnitClashes>();
for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) {
const group = structure.unitSymmetryGroups[i]
if (!Unit.isAtomic(group.units[0])) continue
const group = structure.unitSymmetryGroups[i];
if (!Unit.isAtomic(group.units[0])) continue;
const intraClashes = createIntraUnitClashes(group.units[0], clashes)
const intraClashes = createIntraUnitClashes(group.units[0], clashes);
for (let j = 0, jl = group.units.length; j < jl; ++j) {
intraUnit.set(group.units[j].id, intraClashes)
intraUnit.set(group.units[j].id, intraClashes);
}
}
return {
interUnit: createInterUnitClashes(structure, clashes),
intraUnit
}
};
}
export const ClashesProvider: CustomStructureProperty.Provider<{}, Clashes> = CustomStructureProperty.createProvider({
@@ -315,22 +310,24 @@ export const ClashesProvider: CustomStructureProperty.Provider<{}, Clashes> = Cu
getParams: (data: Structure) => ({}),
isApplicable: (data: Structure) => true,
obtain: async (ctx: CustomProperty.Context, data: Structure) => {
await ValidationReportProvider.attach(ctx, data.models[0])
const validationReport = ValidationReportProvider.get(data.models[0]).value!
return createClashes(data, validationReport.clashes)
await ValidationReportProvider.attach(ctx, data.models[0]);
const validationReport = ValidationReportProvider.get(data.models[0]).value!;
return {
value: createClashes(data, validationReport.clashes)
};
}
})
});
//
function getItem(a: NamedNodeMap, name: string) {
const item = a.getNamedItem(name)
return item !== null ? item.value : ''
const item = a.getNamedItem(name);
return item !== null ? item.value : '';
}
function hasAttr(a: NamedNodeMap, name: string, value: string) {
const item = a.getNamedItem(name)
return item !== null && item.value === value
const item = a.getNamedItem(name);
return item !== null && item.value === value;
}
function getMogInfo(a: NamedNodeMap) {
@@ -339,7 +336,7 @@ function getMogInfo(a: NamedNodeMap) {
obs: parseFloat(getItem(a, 'obsval')),
stdev: parseFloat(getItem(a, 'stdev')),
z: parseFloat(getItem(a, 'Zscore')),
}
};
}
function getMolInfo(a: NamedNodeMap) {
@@ -348,229 +345,229 @@ function getMolInfo(a: NamedNodeMap) {
obs: parseFloat(getItem(a, 'obs')),
stdev: parseFloat(getItem(a, 'stdev')),
z: parseInt(getItem(a, 'z')),
}
};
}
function addIndex(index: number, element: ElementIndex, map: Map<ElementIndex, number[]>) {
if (map.has(element)) map.get(element)!.push(index)
else map.set(element, [index])
if (map.has(element)) map.get(element)!.push(index);
else map.set(element, [index]);
}
function ClashesBuilder(elementsCount: number) {
const aIndices: ElementIndex[] = []
const bIndices: ElementIndex[] = []
const ids: number[] = []
const magnitudes: number[] = []
const distances: number[] = []
const aIndices: ElementIndex[] = [];
const bIndices: ElementIndex[] = [];
const ids: number[] = [];
const magnitudes: number[] = [];
const distances: number[] = [];
const seen = new Map<string, ElementIndex>()
const seen = new Map<string, ElementIndex>();
return {
add(element: ElementIndex, id: number, magnitude: number, distance: number, isSymop: boolean) {
const hash = `${id}|${isSymop ? 's' : ''}`
const other = seen.get(hash)
const hash = `${id}|${isSymop ? 's' : ''}`;
const other = seen.get(hash);
if (other !== undefined) {
aIndices[aIndices.length] = element
bIndices[bIndices.length] = other
ids[ids.length] = id
magnitudes[magnitudes.length] = magnitude
distances[distances.length] = distance
aIndices[aIndices.length] = element;
bIndices[bIndices.length] = other;
ids[ids.length] = id;
magnitudes[magnitudes.length] = magnitude;
distances[distances.length] = distance;
} else {
seen.set(hash, element)
seen.set(hash, element);
}
},
get() {
const builder = new IntAdjacencyGraph.EdgeBuilder(elementsCount, aIndices, bIndices)
const id = new Int32Array(builder.slotCount)
const magnitude = new Float32Array(builder.slotCount)
const distance = new Float32Array(builder.slotCount)
const builder = new IntAdjacencyGraph.EdgeBuilder(elementsCount, aIndices, bIndices);
const id = new Int32Array(builder.slotCount);
const magnitude = new Float32Array(builder.slotCount);
const distance = new Float32Array(builder.slotCount);
for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge()
builder.assignProperty(id, ids[i])
builder.assignProperty(magnitude, magnitudes[i])
builder.assignProperty(distance, distances[i])
builder.addNextEdge();
builder.assignProperty(id, ids[i]);
builder.assignProperty(magnitude, magnitudes[i]);
builder.assignProperty(distance, distances[i]);
}
return builder.createGraph({ id, magnitude, distance })
return builder.createGraph({ id, magnitude, distance });
}
}
};
}
function parseValidationReportXml(xml: XMLDocument, model: Model): ValidationReport {
const rsrz = new Map<ResidueIndex, number>()
const rscc = new Map<ResidueIndex, number>()
const rci = new Map<ResidueIndex, number>()
const geometryIssues = new Map<ResidueIndex, Set<string>>()
const rsrz = new Map<ResidueIndex, number>();
const rscc = new Map<ResidueIndex, number>();
const rci = new Map<ResidueIndex, number>();
const geometryIssues = new Map<ResidueIndex, Set<string>>();
const bondOutliers = {
index: new Map<ElementIndex, number[]>(),
data: [] as ValidationReport['bondOutliers']['data']
}
};
const angleOutliers = {
index: new Map<ElementIndex, number[]>(),
data: [] as ValidationReport['angleOutliers']['data']
}
};
const clashesBuilder = ClashesBuilder(model.atomicHierarchy.atoms._rowCount)
const clashesBuilder = ClashesBuilder(model.atomicHierarchy.atoms._rowCount);
const { index } = model.atomicHierarchy
const { index } = model.atomicHierarchy;
const entries = xml.getElementsByTagName('Entry')
const entries = xml.getElementsByTagName('Entry');
if (entries.length === 1) {
const chemicalShiftLists = entries[0].getElementsByTagName('chemical_shift_list')
const chemicalShiftLists = entries[0].getElementsByTagName('chemical_shift_list');
if (chemicalShiftLists.length === 1) {
const randomCoilIndices = chemicalShiftLists[0].getElementsByTagName('random_coil_index')
const randomCoilIndices = chemicalShiftLists[0].getElementsByTagName('random_coil_index');
for (let j = 0, jl = randomCoilIndices.length; j < jl; ++j) {
const { attributes } = randomCoilIndices[j]
const value = parseFloat(getItem(attributes, 'value'))
const auth_asym_id = getItem(attributes, 'chain')
const auth_comp_id = getItem(attributes, 'rescode')
const auth_seq_id = parseInt(getItem(attributes, 'resnum'))
const rI = index.findResidueAuth({ auth_asym_id, auth_comp_id, auth_seq_id })
if (rI !== -1) rci.set(rI, value)
const { attributes } = randomCoilIndices[j];
const value = parseFloat(getItem(attributes, 'value'));
const auth_asym_id = getItem(attributes, 'chain');
const auth_comp_id = getItem(attributes, 'rescode');
const auth_seq_id = parseInt(getItem(attributes, 'resnum'));
const rI = index.findResidueAuth({ auth_asym_id, auth_comp_id, auth_seq_id });
if (rI !== -1) rci.set(rI, value);
}
}
}
const groups = xml.getElementsByTagName('ModelledSubgroup')
const groups = xml.getElementsByTagName('ModelledSubgroup');
for (let i = 0, il = groups.length; i < il; ++i) {
const g = groups[ i ]
const ga = g.attributes
const g = groups[ i ];
const ga = g.attributes;
const pdbx_PDB_model_num = parseInt(getItem(ga, 'model'))
if (model.modelNum !== pdbx_PDB_model_num) continue
const pdbx_PDB_model_num = parseInt(getItem(ga, 'model'));
if (model.modelNum !== pdbx_PDB_model_num) continue;
const auth_asym_id = getItem(ga, 'chain')
const auth_comp_id = getItem(ga, 'resname')
const auth_seq_id = parseInt(getItem(ga, 'resnum'))
const pdbx_PDB_ins_code = getItem(ga, 'icode').trim() || undefined
const label_alt_id = getItem(ga, 'altcode').trim() || undefined
const auth_asym_id = getItem(ga, 'chain');
const auth_comp_id = getItem(ga, 'resname');
const auth_seq_id = parseInt(getItem(ga, 'resnum'));
const pdbx_PDB_ins_code = getItem(ga, 'icode').trim() || undefined;
const label_alt_id = getItem(ga, 'altcode').trim() || undefined;
const rI = index.findResidueAuth({ auth_asym_id, auth_comp_id, auth_seq_id, pdbx_PDB_ins_code })
const rI = index.findResidueAuth({ auth_asym_id, auth_comp_id, auth_seq_id, pdbx_PDB_ins_code });
// continue if no residue index is found
if (rI === -1) continue
if (rI === -1) continue;
if (ga.getNamedItem('rsrz') !== null) rsrz.set(rI, parseFloat(getItem(ga, 'rsrz')))
if (ga.getNamedItem('rscc') !== null) rscc.set(rI, parseFloat(getItem(ga, 'rscc')))
if (ga.getNamedItem('rsrz') !== null) rsrz.set(rI, parseFloat(getItem(ga, 'rsrz')));
if (ga.getNamedItem('rscc') !== null) rscc.set(rI, parseFloat(getItem(ga, 'rscc')));
const isPolymer = getItem(ga, 'seq') !== '.'
const issues = new Set<string>()
const isPolymer = getItem(ga, 'seq') !== '.';
const issues = new Set<string>();
if (isPolymer) {
const molBondOutliers = g.getElementsByTagName('bond-outlier')
if (molBondOutliers.length) issues.add('bond-outlier')
const molBondOutliers = g.getElementsByTagName('bond-outlier');
if (molBondOutliers.length) issues.add('bond-outlier');
for (let j = 0, jl = molBondOutliers.length; j < jl; ++j) {
const bo = molBondOutliers[j].attributes
const idx = bondOutliers.data.length
const atomA = index.findAtomOnResidue(rI, getItem(bo, 'atom0'))
const atomB = index.findAtomOnResidue(rI, getItem(bo, 'atom1'))
addIndex(idx, atomA, bondOutliers.index)
addIndex(idx, atomB, bondOutliers.index)
const bo = molBondOutliers[j].attributes;
const idx = bondOutliers.data.length;
const atomA = index.findAtomOnResidue(rI, getItem(bo, 'atom0'));
const atomB = index.findAtomOnResidue(rI, getItem(bo, 'atom1'));
addIndex(idx, atomA, bondOutliers.index);
addIndex(idx, atomB, bondOutliers.index);
bondOutliers.data.push({
tag: 'bond-outlier', atomA, atomB, ...getMolInfo(bo)
})
});
}
const molAngleOutliers = g.getElementsByTagName('angle-outlier')
if (molAngleOutliers.length) issues.add('angle-outlier')
const molAngleOutliers = g.getElementsByTagName('angle-outlier');
if (molAngleOutliers.length) issues.add('angle-outlier');
for (let j = 0, jl = molAngleOutliers.length; j < jl; ++j) {
const ao = molAngleOutliers[j].attributes
const idx = bondOutliers.data.length
const atomA = index.findAtomOnResidue(rI, getItem(ao, 'atom0'))
const atomB = index.findAtomOnResidue(rI, getItem(ao, 'atom1'))
const atomC = index.findAtomOnResidue(rI, getItem(ao, 'atom2'))
addIndex(idx, atomA, angleOutliers.index)
addIndex(idx, atomB, angleOutliers.index)
addIndex(idx, atomC, angleOutliers.index)
const ao = molAngleOutliers[j].attributes;
const idx = bondOutliers.data.length;
const atomA = index.findAtomOnResidue(rI, getItem(ao, 'atom0'));
const atomB = index.findAtomOnResidue(rI, getItem(ao, 'atom1'));
const atomC = index.findAtomOnResidue(rI, getItem(ao, 'atom2'));
addIndex(idx, atomA, angleOutliers.index);
addIndex(idx, atomB, angleOutliers.index);
addIndex(idx, atomC, angleOutliers.index);
angleOutliers.data.push({
tag: 'angle-outlier', atomA, atomB, atomC, ...getMolInfo(ao)
})
});
}
const planeOutliers = g.getElementsByTagName('plane-outlier')
if (planeOutliers.length) issues.add('plane-outlier')
const planeOutliers = g.getElementsByTagName('plane-outlier');
if (planeOutliers.length) issues.add('plane-outlier');
if (hasAttr(ga, 'rota', 'OUTLIER')) issues.add('rotamer-outlier')
if (hasAttr(ga, 'rama', 'OUTLIER')) issues.add('ramachandran-outlier')
if (hasAttr(ga, 'RNApucker', 'outlier')) issues.add('RNApucker-outlier')
if (hasAttr(ga, 'rota', 'OUTLIER')) issues.add('rotamer-outlier');
if (hasAttr(ga, 'rama', 'OUTLIER')) issues.add('ramachandran-outlier');
if (hasAttr(ga, 'RNApucker', 'outlier')) issues.add('RNApucker-outlier');
} else {
const mogBondOutliers = g.getElementsByTagName('mog-bond-outlier')
if (mogBondOutliers.length) issues.add('mog-bond-outlier')
const mogBondOutliers = g.getElementsByTagName('mog-bond-outlier');
if (mogBondOutliers.length) issues.add('mog-bond-outlier');
for (let j = 0, jl = mogBondOutliers.length; j < jl; ++j) {
const mbo = mogBondOutliers[j].attributes
const atoms = getItem(mbo, 'atoms').split(',')
const idx = bondOutliers.data.length
const atomA = index.findAtomOnResidue(rI, atoms[0])
const atomB = index.findAtomOnResidue(rI, atoms[1])
addIndex(idx, atomA, bondOutliers.index)
addIndex(idx, atomB, bondOutliers.index)
const mbo = mogBondOutliers[j].attributes;
const atoms = getItem(mbo, 'atoms').split(',');
const idx = bondOutliers.data.length;
const atomA = index.findAtomOnResidue(rI, atoms[0]);
const atomB = index.findAtomOnResidue(rI, atoms[1]);
addIndex(idx, atomA, bondOutliers.index);
addIndex(idx, atomB, bondOutliers.index);
bondOutliers.data.push({
tag: 'mog-bond-outlier', atomA, atomB, ...getMogInfo(mbo)
})
});
}
const mogAngleOutliers = g.getElementsByTagName('mog-angle-outlier')
if (mogAngleOutliers.length) issues.add('mog-angle-outlier')
const mogAngleOutliers = g.getElementsByTagName('mog-angle-outlier');
if (mogAngleOutliers.length) issues.add('mog-angle-outlier');
for (let j = 0, jl = mogAngleOutliers.length; j < jl; ++j) {
const mao = mogAngleOutliers[j].attributes
const atoms = getItem(mao, 'atoms').split(',')
const idx = angleOutliers.data.length
const atomA = index.findAtomOnResidue(rI, atoms[0])
const atomB = index.findAtomOnResidue(rI, atoms[1])
const atomC = index.findAtomOnResidue(rI, atoms[2])
addIndex(idx, atomA, angleOutliers.index)
addIndex(idx, atomB, angleOutliers.index)
addIndex(idx, atomC, angleOutliers.index)
const mao = mogAngleOutliers[j].attributes;
const atoms = getItem(mao, 'atoms').split(',');
const idx = angleOutliers.data.length;
const atomA = index.findAtomOnResidue(rI, atoms[0]);
const atomB = index.findAtomOnResidue(rI, atoms[1]);
const atomC = index.findAtomOnResidue(rI, atoms[2]);
addIndex(idx, atomA, angleOutliers.index);
addIndex(idx, atomB, angleOutliers.index);
addIndex(idx, atomC, angleOutliers.index);
angleOutliers.data.push({
tag: 'mog-angle-outlier', atomA, atomB, atomC, ...getMogInfo(mao)
})
});
}
}
const clashes = g.getElementsByTagName('clash')
if (clashes.length) issues.add('clash')
const clashes = g.getElementsByTagName('clash');
if (clashes.length) issues.add('clash');
for (let j = 0, jl = clashes.length; j < jl; ++j) {
const ca = clashes[j].attributes
const id = parseInt(getItem(ca, 'cid'))
const magnitude = parseFloat(getItem(ca, 'clashmag'))
const distance = parseFloat(getItem(ca, 'dist'))
const label_atom_id = getItem(ca, 'atom')
const element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id)
const ca = clashes[j].attributes;
const id = parseInt(getItem(ca, 'cid'));
const magnitude = parseFloat(getItem(ca, 'clashmag'));
const distance = parseFloat(getItem(ca, 'dist'));
const label_atom_id = getItem(ca, 'atom');
const element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id);
if (element !== -1) {
clashesBuilder.add(element, id, magnitude, distance, false)
clashesBuilder.add(element, id, magnitude, distance, false);
}
}
const symmClashes = g.getElementsByTagName('symm-clash')
if (symmClashes.length) issues.add('symm-clash')
const symmClashes = g.getElementsByTagName('symm-clash');
if (symmClashes.length) issues.add('symm-clash');
for (let j = 0, jl = symmClashes.length; j < jl; ++j) {
const sca = symmClashes[j].attributes
const id = parseInt(getItem(sca, 'scid'))
const magnitude = parseFloat(getItem(sca, 'clashmag'))
const distance = parseFloat(getItem(sca, 'dist'))
const label_atom_id = getItem(sca, 'atom')
const element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id)
const sca = symmClashes[j].attributes;
const id = parseInt(getItem(sca, 'scid'));
const magnitude = parseFloat(getItem(sca, 'clashmag'));
const distance = parseFloat(getItem(sca, 'dist'));
const label_atom_id = getItem(sca, 'atom');
const element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id);
if (element !== -1) {
clashesBuilder.add(element, id, magnitude, distance, true)
clashesBuilder.add(element, id, magnitude, distance, true);
}
}
geometryIssues.set(rI, issues)
geometryIssues.set(rI, issues);
}
const clashes = clashesBuilder.get()
const clashes = clashesBuilder.get();
const validationReport = {
rsrz, rscc, rci, geometryIssues,
bondOutliers, angleOutliers,
clashes
}
};
return validationReport
return validationReport;
}

View File

@@ -13,47 +13,48 @@ import { PickingId } from '../../../mol-geo/geometry/picking';
import { EmptyLoci, Loci, DataLoci } from '../../../mol-model/loci';
import { Interval } from '../../../mol-data/int';
import { RepresentationContext, RepresentationParamsGetter, Representation } from '../../../mol-repr/representation';
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation, getUnitKindsParam } from '../../../mol-repr/structure/representation';
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
import { VisualContext } from '../../../mol-repr/visual';
import { createLinkCylinderMesh, LinkCylinderParams, LinkCylinderStyle } from '../../../mol-repr/structure/visual/util/link';
import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../../../mol-repr/structure/units-visual';
import { VisualUpdateState } from '../../../mol-repr/util';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { ClashesProvider, IntraUnitClashes, InterUnitClashes, ValidationReport } from '../validation-report';
import { CustomProperty } from '../../common/custom-property';
import { ClashesProvider, IntraUnitClashes, InterUnitClashes, ValidationReport } from './prop';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mol-repr/structure/complex-visual';
import { Color } from '../../../mol-util/color';
import { MarkerActions } from '../../../mol-util/marker-action';
import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
import { Sphere3D } from '../../../mol-math/geometry';
import { bondLabel } from '../../../mol-theme/label';
import { getUnitKindsParam } from '../../../mol-repr/structure/params';
//
function createIntraUnitClashCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitClashParams>, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id)
const { edgeCount, a, b, edgeProps } = clashes
const { magnitude } = edgeProps
const { sizeFactor } = props
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id);
const { edgeCount, a, b, edgeProps } = clashes;
const { magnitude } = edgeProps;
const { sizeFactor } = props;
if (!edgeCount) return Mesh.createEmpty(mesh)
if (!edgeCount) return Mesh.createEmpty(mesh);
const { elements } = unit
const pos = unit.conformation.invariantPosition
const { elements } = unit;
const pos = unit.conformation.invariantPosition;
const builderProps = {
linkCount: edgeCount * 2,
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
pos(elements[a[edgeIndex]], posA)
pos(elements[b[edgeIndex]], posB)
pos(elements[a[edgeIndex]], posA);
pos(elements[b[edgeIndex]], posB);
},
style: (edgeIndex: number) => LinkCylinderStyle.Disk,
radius: (edgeIndex: number) => magnitude[edgeIndex] * sizeFactor,
}
};
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
}
export const IntraUnitClashParams = {
@@ -61,7 +62,7 @@ export const IntraUnitClashParams = {
...LinkCylinderParams,
linkCap: PD.Boolean(true),
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.01 }),
}
};
export type IntraUnitClashParams = typeof IntraUnitClashParams
export function IntraUnitClashVisual(materialId: number): UnitsVisual<IntraUnitClashParams> {
@@ -78,95 +79,95 @@ export function IntraUnitClashVisual(materialId: number): UnitsVisual<IntraUnitC
newProps.linkScale !== currentProps.linkScale ||
newProps.linkSpacing !== currentProps.linkSpacing ||
newProps.linkCap !== currentProps.linkCap
)
);
}
}, materialId)
}, materialId);
}
function getIntraClashBoundingSphere(unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[], boundingSphere: Sphere3D) {
return CentroidHelper.fromPairProvider(elements.length, (i, pA, pB) => {
unit.conformation.position(unit.elements[clashes.a[elements[i]]], pA)
unit.conformation.position(unit.elements[clashes.b[elements[i]]], pB)
}, boundingSphere)
unit.conformation.position(unit.elements[clashes.a[elements[i]]], pA);
unit.conformation.position(unit.elements[clashes.b[elements[i]]], pB);
}, boundingSphere);
}
function getIntraClashLabel(structure: Structure, unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
const idx = elements[0]
if (idx === undefined) return ''
const { edgeProps: { id, magnitude, distance } } = clashes
const mag = magnitude[idx].toFixed(2)
const dist = distance[idx].toFixed(2)
const idx = elements[0];
if (idx === undefined) return '';
const { edgeProps: { id, magnitude, distance } } = clashes;
const mag = magnitude[idx].toFixed(2);
const dist = distance[idx].toFixed(2);
return [
`Clash id: ${id[idx]} | Magnitude: ${mag} \u212B | Distance: ${dist} \u212B`,
bondLabel(Bond.Location(structure, unit, clashes.a[idx], structure, unit, clashes.b[idx]))
].join('</br>')
].join('</br>');
}
function IntraClashLoci(structure: Structure, unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
return DataLoci('intra-clashes', { unit, clashes }, elements,
(boundingSphere: Sphere3D) => getIntraClashBoundingSphere(unit, clashes, elements, boundingSphere),
() => getIntraClashLabel(structure, unit, clashes, elements))
() => getIntraClashLabel(structure, unit, clashes, elements));
}
function getIntraClashLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
const { objectId, instanceId, groupId } = pickingId
const { objectId, instanceId, groupId } = pickingId;
if (id === objectId) {
const { structure, group } = structureGroup
const unit = group.units[instanceId]
const { structure, group } = structureGroup;
const unit = group.units[instanceId];
if (Unit.isAtomic(unit)) {
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id)
return IntraClashLoci(structure, unit, clashes, [groupId])
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id);
return IntraClashLoci(structure, unit, clashes, [groupId]);
}
}
return EmptyLoci
return EmptyLoci;
}
function eachIntraClash(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
let changed = false
let changed = false;
// TODO
return changed
return changed;
}
function createIntraClashIterator(structureGroup: StructureGroup): LocationIterator {
const { structure, group } = structureGroup
const unit = group.units[0]
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id)
const { a } = clashes
const groupCount = clashes.edgeCount * 2
const instanceCount = group.units.length
const location = StructureElement.Location.create(structure)
const { structure, group } = structureGroup;
const unit = group.units[0];
const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id);
const { a } = clashes;
const groupCount = clashes.edgeCount * 2;
const instanceCount = group.units.length;
const location = StructureElement.Location.create(structure);
const getLocation = (groupIndex: number, instanceIndex: number) => {
const unit = group.units[instanceIndex]
location.unit = unit
location.element = unit.elements[a[groupIndex]]
return location
}
return LocationIterator(groupCount, instanceCount, getLocation)
const unit = group.units[instanceIndex];
location.unit = unit;
location.element = unit.elements[a[groupIndex]];
return location;
};
return LocationIterator(groupCount, instanceCount, getLocation);
}
//
function createInterUnitClashCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitClashParams>, mesh?: Mesh) {
const clashes = ClashesProvider.get(structure).value!.interUnit
const { edges, edgeCount } = clashes
const { sizeFactor } = props
const clashes = ClashesProvider.get(structure).value!.interUnit;
const { edges, edgeCount } = clashes;
const { sizeFactor } = props;
if (!edgeCount) return Mesh.createEmpty(mesh)
if (!edgeCount) return Mesh.createEmpty(mesh);
const builderProps = {
linkCount: edgeCount,
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
const b = edges[edgeIndex]
const uA = b.unitA, uB = b.unitB
uA.conformation.position(uA.elements[b.indexA], posA)
uB.conformation.position(uB.elements[b.indexB], posB)
const b = edges[edgeIndex];
const uA = b.unitA, uB = b.unitB;
uA.conformation.position(uA.elements[b.indexA], posA);
uB.conformation.position(uB.elements[b.indexB], posB);
},
style: (edgeIndex: number) => LinkCylinderStyle.Disk,
radius: (edgeIndex: number) => edges[edgeIndex].props.magnitude * sizeFactor
}
};
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
}
export const InterUnitClashParams = {
@@ -174,7 +175,7 @@ export const InterUnitClashParams = {
...LinkCylinderParams,
linkCap: PD.Boolean(true),
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.01 }),
}
};
export type InterUnitClashParams = typeof InterUnitClashParams
export function InterUnitClashVisual(materialId: number): ComplexVisual<InterUnitClashParams> {
@@ -191,65 +192,65 @@ export function InterUnitClashVisual(materialId: number): ComplexVisual<InterUni
newProps.linkScale !== currentProps.linkScale ||
newProps.linkSpacing !== currentProps.linkSpacing ||
newProps.linkCap !== currentProps.linkCap
)
);
}
}, materialId)
}, materialId);
}
function getInterClashBoundingSphere(clashes: InterUnitClashes, elements: number[], boundingSphere: Sphere3D) {
return CentroidHelper.fromPairProvider(elements.length, (i, pA, pB) => {
const c = clashes.edges[elements[i]]
c.unitA.conformation.position(c.unitA.elements[c.indexA], pA)
c.unitB.conformation.position(c.unitB.elements[c.indexB], pB)
}, boundingSphere)
const c = clashes.edges[elements[i]];
c.unitA.conformation.position(c.unitA.elements[c.indexA], pA);
c.unitB.conformation.position(c.unitB.elements[c.indexB], pB);
}, boundingSphere);
}
function getInterClashLabel(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
const idx = elements[0]
if (idx === undefined) return ''
const c = clashes.edges[idx]
const mag = c.props.magnitude.toFixed(2)
const dist = c.props.distance.toFixed(2)
const idx = elements[0];
if (idx === undefined) return '';
const c = clashes.edges[idx];
const mag = c.props.magnitude.toFixed(2);
const dist = c.props.distance.toFixed(2);
return [
`Clash id: ${c.props.id} | Magnitude: ${mag} \u212B | Distance: ${dist} \u212B`,
bondLabel(Bond.Location(structure, c.unitA, c.indexA, structure, c.unitB, c.indexB))
].join('</br>')
].join('</br>');
}
function InterClashLoci(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
return DataLoci('inter-clashes', clashes, elements,
(boundingSphere: Sphere3D) => getInterClashBoundingSphere(clashes, elements, boundingSphere),
() => getInterClashLabel(structure, clashes, elements))
() => getInterClashLabel(structure, clashes, elements));
}
function getInterClashLoci(pickingId: PickingId, structure: Structure, id: number) {
const { objectId, groupId } = pickingId
const { objectId, groupId } = pickingId;
if (id === objectId) {
const clashes = ClashesProvider.get(structure).value!.interUnit
return InterClashLoci(structure, clashes, [groupId])
const clashes = ClashesProvider.get(structure).value!.interUnit;
return InterClashLoci(structure, clashes, [groupId]);
}
return EmptyLoci
return EmptyLoci;
}
function eachInterClash(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
let changed = false
let changed = false;
// TODO
return changed
return changed;
}
function createInterClashIterator(structure: Structure): LocationIterator {
const clashes = ClashesProvider.get(structure).value!.interUnit
const groupCount = clashes.edgeCount
const instanceCount = 1
const location = StructureElement.Location.create(structure)
const clashes = ClashesProvider.get(structure).value!.interUnit;
const groupCount = clashes.edgeCount;
const instanceCount = 1;
const location = StructureElement.Location.create(structure);
const getLocation = (groupIndex: number) => {
const clash = clashes.edges[groupIndex]
location.unit = clash.unitA
location.element = clash.unitA.elements[clash.indexA]
return location
}
return LocationIterator(groupCount, instanceCount, getLocation, true)
const clash = clashes.edges[groupIndex];
location.unit = clash.unitA;
location.element = clash.unitA.elements[clash.indexA];
return location;
};
return LocationIterator(groupCount, instanceCount, getLocation, true);
}
//
@@ -257,24 +258,24 @@ function createInterClashIterator(structure: Structure): LocationIterator {
const ClashesVisuals = {
'intra-clash': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, IntraUnitClashParams>) => UnitsRepresentation('Intra-unit clash cylinder', ctx, getParams, IntraUnitClashVisual),
'inter-clash': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, InterUnitClashParams>) => ComplexRepresentation('Inter-unit clash cylinder', ctx, getParams, InterUnitClashVisual),
}
};
export const ClashesParams = {
...IntraUnitClashParams,
...InterUnitClashParams,
unitKinds: getUnitKindsParam(['atomic']),
visuals: PD.MultiSelect(['intra-clash', 'inter-clash'], PD.objectToOptions(ClashesVisuals))
}
};
export type ClashesParams = typeof ClashesParams
export function getClashesParams(ctx: ThemeRegistryContext, structure: Structure) {
return PD.clone(ClashesParams)
return PD.clone(ClashesParams);
}
export type ClashesRepresentation = StructureRepresentation<ClashesParams>
export function ClashesRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ClashesParams>): ClashesRepresentation {
const repr = Representation.createMulti('Clashes', ctx, getParams, StructureRepresentationStateBuilder, ClashesVisuals as unknown as Representation.Def<Structure, ClashesParams>)
repr.setState({ markerActions: MarkerActions.Highlighting })
return repr
const repr = Representation.createMulti('Clashes', ctx, getParams, StructureRepresentationStateBuilder, ClashesVisuals as unknown as Representation.Def<Structure, ClashesParams>);
repr.setState({ markerActions: MarkerActions.Highlighting });
return repr;
}
export const ClashesRepresentationProvider = StructureRepresentationProvider({
@@ -291,4 +292,4 @@ export const ClashesRepresentationProvider = StructureRepresentationProvider({
attach: (ctx: CustomProperty.Context, structure: Structure) => ClashesProvider.attach(ctx, structure, void 0, true),
detach: (data) => ClashesProvider.ref(data, false)
}
})
});

View File

@@ -5,12 +5,12 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra';
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { CameraTransitionManager } from './camera/transition';
import { BehaviorSubject } from 'rxjs';
export { Camera }
export { Camera };
class Camera {
readonly view: Mat4 = Mat4.identity();
@@ -66,8 +66,8 @@ class Camera {
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
if (changed) {
Mat4.mul(this.projectionView, this.projection, this.view)
Mat4.invert(this.inverseProjectionView, this.projectionView)
Mat4.mul(this.projectionView, this.projection, this.view);
Mat4.invert(this.inverseProjectionView, this.projectionView);
Mat4.copy(this.prevView, this.view);
Mat4.copy(this.prevProjection, this.projection);
@@ -86,30 +86,30 @@ class Camera {
}
getTargetDistance(radius: number) {
const r = Math.max(radius, 0.01)
const { fov } = this.state
const { width, height } = this.viewport
const aspect = width / height
const aspectFactor = (height < width ? 1 : aspect)
return Math.abs((r / aspectFactor) / Math.sin(fov / 2))
const r = Math.max(radius, 0.01);
const { fov } = this.state;
const { width, height } = this.viewport;
const aspect = width / height;
const aspectFactor = (height < width ? 1 : aspect);
return Math.abs((r / aspectFactor) / Math.sin(fov / 2));
}
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
const r = Math.max(radius, 0.01)
const targetDistance = this.getTargetDistance(r)
const r = Math.max(radius, 0.01);
const targetDistance = this.getTargetDistance(r);
Vec3.sub(this.deltaDirection, this.target, this.position)
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance)
Vec3.sub(this.newPosition, target, this.deltaDirection)
Vec3.sub(this.deltaDirection, this.target, this.position);
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection);
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance);
Vec3.sub(this.newPosition, target, this.deltaDirection);
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
state.target = Vec3.clone(target)
state.radius = r
state.position = Vec3.clone(this.newPosition)
if (up) Vec3.matchDirection(state.up, up, state.up)
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
state.target = Vec3.clone(target);
state.radius = r;
state.position = Vec3.clone(this.newPosition);
if (up) Vec3.matchDirection(state.up, up, state.up);
return state
return state;
}
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
@@ -119,11 +119,11 @@ class Camera {
}
project(out: Vec4, point: Vec3) {
return cameraProject(out, point, this.viewport, this.projectionView)
return cameraProject(out, point, this.viewport, this.projectionView);
}
unproject(out: Vec3, point: Vec3) {
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView)
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView);
}
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) {
@@ -151,12 +151,12 @@ namespace Camera {
}
export function setViewOffset(out: ViewOffset, fullWidth: number, fullHeight: number, offsetX: number, offsetY: number, width: number, height: number) {
out.fullWidth = fullWidth
out.fullHeight = fullHeight
out.offsetX = offsetX
out.offsetY = offsetY
out.width = width
out.height = height
out.fullWidth = fullWidth;
out.fullHeight = fullHeight;
out.offsetX = offsetX;
out.offsetY = offsetY;
out.width = width;
out.height = height;
}
export function createDefaultSnapshot(): Snapshot {
@@ -209,90 +209,90 @@ namespace Camera {
}
function updateOrtho(camera: Camera) {
const { viewport, zoom, near, far, viewOffset } = camera
const { viewport, zoom, near, far, viewOffset } = camera;
const fullLeft = -(viewport.width - viewport.x) / 2
const fullRight = (viewport.width - viewport.x) / 2
const fullTop = (viewport.height - viewport.y) / 2
const fullBottom = -(viewport.height - viewport.y) / 2
const fullLeft = -(viewport.width - viewport.x) / 2;
const fullRight = (viewport.width - viewport.x) / 2;
const fullTop = (viewport.height - viewport.y) / 2;
const fullBottom = -(viewport.height - viewport.y) / 2;
const dx = (fullRight - fullLeft) / (2 * zoom)
const dy = (fullTop - fullBottom) / (2 * zoom)
const cx = (fullRight + fullLeft) / 2
const cy = (fullTop + fullBottom) / 2
const dx = (fullRight - fullLeft) / (2 * zoom);
const dy = (fullTop - fullBottom) / (2 * zoom);
const cx = (fullRight + fullLeft) / 2;
const cy = (fullTop + fullBottom) / 2;
let left = cx - dx
let right = cx + dx
let top = cy + dy
let bottom = cy - dy
let left = cx - dx;
let right = cx + dx;
let top = cy + dy;
let bottom = cy - dy;
if (viewOffset.enabled) {
const zoomW = zoom / (viewOffset.width / viewOffset.fullWidth)
const zoomH = zoom / (viewOffset.height / viewOffset.fullHeight)
const scaleW = (fullRight - fullLeft) / viewOffset.width
const scaleH = (fullTop - fullBottom) / viewOffset.height
left += scaleW * (viewOffset.offsetX / zoomW)
right = left + scaleW * (viewOffset.width / zoomW)
top -= scaleH * (viewOffset.offsetY / zoomH)
bottom = top - scaleH * (viewOffset.height / zoomH)
const zoomW = zoom / (viewOffset.width / viewOffset.fullWidth);
const zoomH = zoom / (viewOffset.height / viewOffset.fullHeight);
const scaleW = (fullRight - fullLeft) / viewOffset.width;
const scaleH = (fullTop - fullBottom) / viewOffset.height;
left += scaleW * (viewOffset.offsetX / zoomW);
right = left + scaleW * (viewOffset.width / zoomW);
top -= scaleH * (viewOffset.offsetY / zoomH);
bottom = top - scaleH * (viewOffset.height / zoomH);
}
// build projection matrix
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
Mat4.ortho(camera.projection, left, right, top, bottom, near, far);
// build view matrix
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up);
}
function updatePers(camera: Camera) {
const aspect = camera.viewport.width / camera.viewport.height
const aspect = camera.viewport.width / camera.viewport.height;
const { near, far, viewOffset } = camera
const { near, far, viewOffset } = camera;
let top = near * Math.tan(0.5 * camera.state.fov)
let height = 2 * top
let width = aspect * height
let left = -0.5 * width
let top = near * Math.tan(0.5 * camera.state.fov);
let height = 2 * top;
let width = aspect * height;
let left = -0.5 * width;
if (viewOffset.enabled) {
left += viewOffset.offsetX * width / viewOffset.fullWidth
top -= viewOffset.offsetY * height / viewOffset.fullHeight
width *= viewOffset.width / viewOffset.fullWidth
height *= viewOffset.height / viewOffset.fullHeight
left += viewOffset.offsetX * width / viewOffset.fullWidth;
top -= viewOffset.offsetY * height / viewOffset.fullHeight;
width *= viewOffset.width / viewOffset.fullWidth;
height *= viewOffset.height / viewOffset.fullHeight;
}
// build projection matrix
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far)
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far);
// build view matrix
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up);
}
function updateClip(camera: Camera) {
let { radius, radiusMax, mode, fog, clipFar } = camera.state
if (radius < 0.01) radius = 0.01
let { radius, radiusMax, mode, fog, clipFar } = camera.state;
if (radius < 0.01) radius = 0.01;
const normalizedFar = clipFar ? radius : radiusMax
const cameraDistance = Vec3.distance(camera.position, camera.target)
let near = cameraDistance - radius
let far = cameraDistance + normalizedFar
const normalizedFar = clipFar ? radius : radiusMax;
const cameraDistance = Vec3.distance(camera.position, camera.target);
let near = cameraDistance - radius;
let far = cameraDistance + normalizedFar;
const fogNearFactor = -(50 - fog) / 50
let fogNear = cameraDistance - (normalizedFar * fogNearFactor)
let fogFar = far
const fogNearFactor = -(50 - fog) / 50;
let fogNear = cameraDistance - (normalizedFar * fogNearFactor);
let fogFar = far;
if (mode === 'perspective') {
// set at least to 5 to avoid slow sphere impostor rendering
near = Math.max(5, near)
far = Math.max(5, far)
near = Math.max(5, near);
far = Math.max(5, far);
} else {
near = Math.max(0, near)
far = Math.max(0, far)
near = Math.max(0, near);
far = Math.max(0, far);
}
if (near === far) {
// make sure near and far are not identical to avoid Infinity in the projection matrix
far = near + 0.01
far = near + 0.01;
}
camera.near = near;

View File

@@ -8,7 +8,7 @@ import { Camera } from '../camera';
import { Quat, Vec3 } from '../../mol-math/linear-algebra';
import { lerp } from '../../mol-math/interpolate';
export { CameraTransitionManager }
export { CameraTransitionManager };
class CameraTransitionManager {
private t = 0;
@@ -21,8 +21,8 @@ class CameraTransitionManager {
private _target: Camera.Snapshot = Camera.createDefaultSnapshot();
private _current = Camera.createDefaultSnapshot();
get source(): Readonly<Camera.Snapshot> { return this._source }
get target(): Readonly<Camera.Snapshot> { return this._target }
get source(): Readonly<Camera.Snapshot> { return this._source; }
get target(): Readonly<Camera.Snapshot> { return this._target; }
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
if (!this.inTransition || durationMs > 0) {
@@ -36,7 +36,7 @@ class CameraTransitionManager {
Camera.copySnapshot(this._target, to);
if (this._target.radius > this._target.radiusMax) {
this._target.radius = this._target.radiusMax
this._target.radius = this._target.radiusMax;
}
if (!this.inTransition && durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {

View File

@@ -4,9 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra'
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra';
export { Viewport }
export { Viewport };
type Viewport = {
x: number
@@ -16,74 +16,74 @@ type Viewport = {
}
function Viewport() {
return Viewport.zero()
return Viewport.zero();
}
namespace Viewport {
export function zero(): Viewport {
return { x: 0, y: 0, width: 0, height: 0 }
return { x: 0, y: 0, width: 0, height: 0 };
}
export function create(x: number, y: number, width: number, height: number): Viewport {
return { x, y, width, height }
return { x, y, width, height };
}
export function clone(viewport: Viewport): Viewport {
return { ...viewport }
return { ...viewport };
}
export function copy(target: Viewport, source: Viewport): Viewport {
return Object.assign(target, source)
return Object.assign(target, source);
}
export function set(viewport: Viewport, x: number, y: number, width: number, height: number): Viewport {
viewport.x = x
viewport.y = y
viewport.width = width
viewport.height = height
return viewport
viewport.x = x;
viewport.y = y;
viewport.width = width;
viewport.height = height;
return viewport;
}
export function toVec4(v4: Vec4, viewport: Viewport): Vec4 {
v4[0] = viewport.x
v4[1] = viewport.y
v4[2] = viewport.width
v4[3] = viewport.height
return v4
v4[0] = viewport.x;
v4[1] = viewport.y;
v4[2] = viewport.width;
v4[3] = viewport.height;
return v4;
}
export function equals(a: Viewport, b: Viewport) {
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
}
}
//
const NEAR_RANGE = 0
const FAR_RANGE = 1
const NEAR_RANGE = 0;
const FAR_RANGE = 1;
const tmpVec4 = Vec4()
const tmpVec4 = Vec4();
/** Transform point into 2D window coordinates. */
export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
// clip space -> NDC -> window coordinates, implicit 1.0 for w component
Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0)
Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0);
// transform into clip space
Vec4.transformMat4(tmpVec4, tmpVec4, projectionView)
Vec4.transformMat4(tmpVec4, tmpVec4, projectionView);
// transform into NDC
const w = tmpVec4[3]
const w = tmpVec4[3];
if (w !== 0) {
tmpVec4[0] /= w
tmpVec4[1] /= w
tmpVec4[2] /= w
tmpVec4[0] /= w;
tmpVec4[1] /= w;
tmpVec4[2] /= w;
}
// transform into window coordinates, set fourth component is (1/clip.w) as in gl_FragCoord.w
out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2)
out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2)
out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2
out[3] = w === 0 ? 0 : 1 / w
return out
out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2);
out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2);
out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2;
out[3] = w === 0 ? 0 : 1 / w;
return out;
}
/**
@@ -91,14 +91,14 @@ export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, proje
* The point must have x and y set to 2D window coordinates and z between 0 (near) and 1 (far).
*/
export function cameraUnproject (out: Vec3, point: Vec3, viewport: Viewport, inverseProjectionView: Mat4) {
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
const x = point[0] - vX
const y = (vHeight - point[1] - 1) - vY
const z = point[2]
const x = point[0] - vX;
const y = (vHeight - point[1] - 1) - vY;
const z = point[2];
out[0] = (2 * x) / vWidth - 1
out[1] = (2 * y) / vHeight - 1
out[2] = 2 * z - 1
return Vec3.transformMat4(out, out, inverseProjectionView)
out[0] = (2 * x) / vWidth - 1;
out[1] = (2 * y) / vHeight - 1;
out[2] = 2 * z - 1;
return Vec3.transformMat4(out, out, inverseProjectionView);
}

View File

@@ -7,12 +7,12 @@
import { BehaviorSubject, Subscription } from 'rxjs';
import { now } from '../mol-util/now';
import { Vec3 } from '../mol-math/linear-algebra'
import InputObserver, { ModifiersKeys, ButtonsType } from '../mol-util/input/input-observer'
import Renderer, { RendererStats, RendererParams } from '../mol-gl/renderer'
import { GraphicsRenderObject } from '../mol-gl/render-object'
import { TrackballControls, TrackballControlsParams } from './controls/trackball'
import { Viewport } from './camera/util'
import { Vec3 } from '../mol-math/linear-algebra';
import InputObserver, { ModifiersKeys, ButtonsType } from '../mol-util/input/input-observer';
import Renderer, { RendererStats, RendererParams } from '../mol-gl/renderer';
import { GraphicsRenderObject } from '../mol-gl/render-object';
import { TrackballControls, TrackballControlsParams } from './controls/trackball';
import { Viewport } from './camera/util';
import { createContext, WebGLContext, getGLContext } from '../mol-gl/webgl/context';
import { Representation } from '../mol-repr/representation';
import Scene from '../mol-gl/scene';
@@ -35,6 +35,7 @@ import { ImagePass, ImageProps } from './passes/image';
import { Sphere3D } from '../mol-math/geometry';
import { isDebugMode } from '../mol-util/debug';
import { CameraHelperParams } from './helper/camera-helper';
import { produce } from 'immer';
export const Canvas3DParams = {
camera: PD.Group({
@@ -60,11 +61,11 @@ export const Canvas3DParams = {
renderer: PD.Group(RendererParams),
trackball: PD.Group(TrackballControlsParams),
debug: PD.Group(DebugHelperParams)
}
};
export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
export { Canvas3D }
export { Canvas3D };
interface Canvas3D {
readonly webgl: WebGLContext,
@@ -93,9 +94,8 @@ interface Canvas3D {
requestCameraReset(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
readonly camera: Camera
readonly boundingSphere: Readonly<Sphere3D>
downloadScreenshot(): void
getPixelData(variant: GraphicsRenderVariant): PixelData
setProps(props: Partial<Canvas3DProps>): void
setProps(props: Partial<Canvas3DProps> | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void)): void
getImagePass(props: Partial<ImageProps>): ImagePass
/** Returns a copy of the current Canvas3D instance props */
@@ -107,7 +107,7 @@ interface Canvas3D {
dispose(): void
}
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()));
namespace Canvas3D {
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
@@ -120,144 +120,139 @@ namespace Canvas3D {
depth: true,
preserveDrawingBuffer: true,
premultipliedAlpha: false,
})
if (gl === null) throw new Error('Could not create a WebGL rendering context')
const input = InputObserver.fromElement(canvas)
const webgl = createContext(gl)
});
if (gl === null) throw new Error('Could not create a WebGL rendering context');
const input = InputObserver.fromElement(canvas);
const webgl = createContext(gl);
if (isDebugMode) {
const loseContextExt = gl.getExtension('WEBGL_lose_context')
const loseContextExt = gl.getExtension('WEBGL_lose_context');
if (loseContextExt) {
canvas.addEventListener('mousedown', e => {
if (webgl.isContextLost) return
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return
if (webgl.isContextLost) return;
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return;
console.log('lose context')
loseContextExt.loseContext()
console.log('lose context');
loseContextExt.loseContext();
setTimeout(() => {
if (!webgl.isContextLost) return
console.log('restore context')
loseContextExt.restoreContext()
}, 1000)
}, false)
if (!webgl.isContextLost) return;
console.log('restore context');
loseContextExt.restoreContext();
}, 1000);
}, false);
}
}
// https://www.khronos.org/webgl/wiki/HandlingContextLost
canvas.addEventListener('webglcontextlost', e => {
webgl.setContextLost()
e.preventDefault()
if (isDebugMode) console.log('context lost')
}, false)
webgl.setContextLost();
e.preventDefault();
if (isDebugMode) console.log('context lost');
}, false);
canvas.addEventListener('webglcontextrestored', () => {
if (!webgl.isContextLost) return
webgl.handleContextRestored()
if (isDebugMode) console.log('context restored')
}, false)
if (!webgl.isContextLost) return;
webgl.handleContextRestored();
if (isDebugMode) console.log('context restored');
}, false);
return Canvas3D.create(webgl, input, props)
return Canvas3D.create(webgl, input, props);
}
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}): Canvas3D {
const p = { ...DefaultCanvas3DParams, ...props }
const p = { ...DefaultCanvas3DParams, ...props };
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>()
const reprCount = new BehaviorSubject(0)
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();
const reprCount = new BehaviorSubject(0);
const startTime = now()
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
const startTime = now();
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
const { gl, contextRestored } = webgl
const { gl, contextRestored } = webgl;
let width = gl.drawingBufferWidth
let height = gl.drawingBufferHeight
let width = gl.drawingBufferWidth;
let height = gl.drawingBufferHeight;
const scene = Scene.create(webgl)
const scene = Scene.create(webgl);
const camera = new Camera({
position: Vec3.create(0, 0, 100),
mode: p.camera.mode,
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
clipFar: p.cameraClipping.far
})
});
const controls = TrackballControls.create(input, camera, p.trackball)
const renderer = Renderer.create(webgl, p.renderer)
const controls = TrackballControls.create(input, camera, p.trackball);
const renderer = Renderer.create(webgl, p.renderer);
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, {
cameraHelper: p.camera.helper
})
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
});
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5);
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing);
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample);
const contextRestoredSub = contextRestored.subscribe(() => {
pickPass.pickDirty = true
draw(true)
})
let drawPending = false
let cameraResetRequested = false
let nextCameraResetDuration: number | undefined = void 0
let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0
let drawPending = false;
let cameraResetRequested = false;
let nextCameraResetDuration: number | undefined = void 0;
let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0;
function getLoci(pickingId: PickingId) {
let loci: Loci = EmptyLoci
let repr: Representation.Any = Representation.Empty
let loci: Loci = EmptyLoci;
let repr: Representation.Any = Representation.Empty;
reprRenderObjects.forEach((_, _repr) => {
const _loci = _repr.getLoci(pickingId)
const _loci = _repr.getLoci(pickingId);
if (!isEmptyLoci(_loci)) {
if (!isEmptyLoci(loci)) {
console.warn('found another loci, this should not happen')
console.warn('found another loci, this should not happen');
}
loci = _loci
repr = _repr
loci = _loci;
repr = _repr;
}
})
return { loci, repr }
});
return { loci, repr };
}
function mark(reprLoci: Representation.Loci, action: MarkerAction) {
const { repr, loci } = reprLoci
let changed = false
const { repr, loci } = reprLoci;
let changed = false;
if (repr) {
changed = repr.mark(loci, action)
changed = repr.mark(loci, action);
} else {
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed })
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed) {
scene.update(void 0, true)
const prevPickDirty = pickPass.pickDirty
draw(true)
pickPass.pickDirty = prevPickDirty // marking does not change picking buffers
scene.update(void 0, true);
const prevPickDirty = pickPass.pickDirty;
draw(true);
pickPass.pickDirty = prevPickDirty; // marking does not change picking buffers
}
}
function render(force: boolean) {
if (webgl.isContextLost) return false
if (webgl.isContextLost) return false;
let didRender = false
controls.update(currentTime)
Viewport.set(camera.viewport, 0, 0, width, height)
const cameraChanged = camera.update()
multiSample.update(force || cameraChanged, currentTime)
let didRender = false;
controls.update(currentTime);
Viewport.set(camera.viewport, 0, 0, width, height);
const cameraChanged = camera.update();
multiSample.update(force || cameraChanged, currentTime);
if (force || cameraChanged || multiSample.enabled) {
renderer.setViewport(0, 0, width, height)
renderer.setViewport(0, 0, width, height);
if (multiSample.enabled) {
multiSample.render(true, p.transparentBackground)
multiSample.render(true, p.transparentBackground);
} else {
drawPass.render(!postprocessing.enabled, p.transparentBackground)
if (postprocessing.enabled) postprocessing.render(true)
drawPass.render(!postprocessing.enabled, p.transparentBackground);
if (postprocessing.enabled) postprocessing.render(true);
}
pickPass.pickDirty = true
didRender = true
pickPass.pickDirty = true;
didRender = true;
}
return didRender;
@@ -268,15 +263,15 @@ namespace Canvas3D {
function draw(force?: boolean) {
if (render(!!force || forceNextDraw)) {
didDraw.next(now() - startTime as now.Timestamp)
didDraw.next(now() - startTime as now.Timestamp);
}
forceNextDraw = false;
drawPending = false
drawPending = false;
}
function requestDraw(force?: boolean) {
if (drawPending) return
drawPending = true
if (drawPending) return;
drawPending = true;
forceNextDraw = !!force;
}
@@ -289,11 +284,11 @@ namespace Canvas3D {
if (!camera.transition.inTransition && !webgl.isContextLost) {
interactionHelper.tick(currentTime);
}
requestAnimationFrame(animate)
requestAnimationFrame(animate);
}
function identify(x: number, y: number): PickingId | undefined {
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
return webgl.isContextLost ? undefined : pickPass.identify(x, y);
}
function commit(isSynchronous: boolean = false) {
@@ -307,7 +302,7 @@ namespace Canvas3D {
const { center, radius } = scene.boundingSphereVisible;
if (radius > 0) {
const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration
const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration;
const focus = camera.getFocus(center, radius);
const snapshot = nextCameraResetSnapshot ? { ...focus, ...nextCameraResetSnapshot } : focus;
camera.setState(snapshot, duration);
@@ -326,8 +321,8 @@ namespace Canvas3D {
if (camera.transition.inTransition || nextCameraResetSnapshot) return false;
let cameraSphereOverlapsNone = true
Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius)
let cameraSphereOverlapsNone = true;
Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius);
// check if any renderable has moved outside of the old bounding sphere
// and if no renderable is overlapping with the camera sphere
@@ -359,7 +354,7 @@ namespace Canvas3D {
}
if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0)
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
reprCount.next(reprRenderObjects.size);
return true;
@@ -368,31 +363,31 @@ namespace Canvas3D {
function add(repr: Representation.Any) {
registerAutoUpdate(repr);
const oldRO = reprRenderObjects.get(repr)
const newRO = new Set<GraphicsRenderObject>()
repr.renderObjects.forEach(o => newRO.add(o))
const oldRO = reprRenderObjects.get(repr);
const newRO = new Set<GraphicsRenderObject>();
repr.renderObjects.forEach(o => newRO.add(o));
if (oldRO) {
if (!SetUtils.areEqual(newRO, oldRO)) {
newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o) })
oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o) })
newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o); });
oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o); });
}
} else {
repr.renderObjects.forEach(o => scene.add(o))
repr.renderObjects.forEach(o => scene.add(o));
}
reprRenderObjects.set(repr, newRO)
reprRenderObjects.set(repr, newRO);
scene.update(repr.renderObjects, false)
scene.update(repr.renderObjects, false);
}
function remove(repr: Representation.Any) {
unregisterAutoUpdate(repr);
const renderObjects = reprRenderObjects.get(repr)
const renderObjects = reprRenderObjects.get(repr);
if (renderObjects) {
renderObjects.forEach(o => scene.remove(o))
reprRenderObjects.delete(repr)
scene.update(repr.renderObjects, false, true)
renderObjects.forEach(o => scene.remove(o));
reprRenderObjects.delete(repr);
scene.update(repr.renderObjects, false, true);
}
}
@@ -401,7 +396,7 @@ namespace Canvas3D {
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
if (!repr.state.syncManually) add(repr);
}))
}));
}
function unregisterAutoUpdate(repr: Representation.Any) {
@@ -412,7 +407,37 @@ namespace Canvas3D {
}
}
handleResize()
function getProps(): Canvas3DProps {
const radius = scene.boundingSphere.radius > 0
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
: 0;
return {
camera: {
mode: camera.state.mode,
helper: { ...drawPass.props.cameraHelper }
},
cameraFog: camera.state.fog > 0
? { name: 'on' as const, params: { intensity: camera.state.fog } }
: { name: 'off' as const, params: {} },
cameraClipping: { far: camera.state.clipFar, radius },
cameraResetDurationMs: p.cameraResetDurationMs,
transparentBackground: p.transparentBackground,
postprocessing: { ...postprocessing.props },
multiSample: { ...multiSample.props },
renderer: { ...renderer.props },
trackball: { ...controls.props },
debug: { ...debugHelper.props }
};
}
handleResize();
const contextRestoredSub = contextRestored.subscribe(() => {
pickPass.pickDirty = true;
draw(true);
});
return {
webgl,
@@ -425,26 +450,26 @@ namespace Canvas3D {
if (!reprRenderObjects.has(repr)) return;
scene.update(repr.renderObjects, !!keepSphere);
} else {
scene.update(void 0, !!keepSphere)
scene.update(void 0, !!keepSphere);
}
},
clear: () => {
reprUpdatedSubscriptions.forEach(v => v.unsubscribe())
reprUpdatedSubscriptions.clear()
reprRenderObjects.clear()
scene.clear()
debugHelper.clear()
requestDraw(true)
reprCount.next(reprRenderObjects.size)
reprUpdatedSubscriptions.forEach(v => v.unsubscribe());
reprUpdatedSubscriptions.clear();
reprRenderObjects.clear();
scene.clear();
debugHelper.clear();
requestDraw(true);
reprCount.next(reprRenderObjects.size);
},
syncVisibility: () => {
if (camera.state.radiusMax === 0) {
cameraResetRequested = true
nextCameraResetDuration = 0
cameraResetRequested = true;
nextCameraResetDuration = 0;
}
if (scene.syncVisibility()) {
if (debugHelper.isEnabled) debugHelper.update()
if (debugHelper.isEnabled) debugHelper.update();
}
},
@@ -463,63 +488,64 @@ namespace Canvas3D {
},
camera,
boundingSphere: scene.boundingSphere,
downloadScreenshot: () => {
// TODO
},
getPixelData: (variant: GraphicsRenderVariant) => {
switch (variant) {
case 'color': return webgl.getDrawingBufferPixelData()
case 'pickObject': return pickPass.objectPickTarget.getPixelData()
case 'pickInstance': return pickPass.instancePickTarget.getPixelData()
case 'pickGroup': return pickPass.groupPickTarget.getPixelData()
case 'depth': return readTexture(webgl, drawPass.depthTexture) as PixelData
case 'color': return webgl.getDrawingBufferPixelData();
case 'pickObject': return pickPass.objectPickTarget.getPixelData();
case 'pickInstance': return pickPass.instancePickTarget.getPixelData();
case 'pickGroup': return pickPass.groupPickTarget.getPixelData();
case 'depth': return readTexture(webgl, drawPass.depthTexture) as PixelData;
}
},
didDraw,
reprCount,
setProps: (props: Partial<Canvas3DProps>) => {
const cameraState: Partial<Camera.Snapshot> = Object.create(null)
setProps: (properties) => {
const props: Partial<Canvas3DProps> = typeof properties === 'function'
? produce(getProps(), properties)
: properties;
const cameraState: Partial<Camera.Snapshot> = Object.create(null);
if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) {
cameraState.mode = props.camera.mode
cameraState.mode = props.camera.mode;
}
if (props.cameraFog !== undefined) {
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0
if (newFog !== camera.state.fog) cameraState.fog = newFog
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
if (newFog !== camera.state.fog) cameraState.fog = newFog;
}
if (props.cameraClipping !== undefined) {
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
cameraState.clipFar = props.cameraClipping.far
cameraState.clipFar = props.cameraClipping.far;
}
if (props.cameraClipping.radius !== undefined) {
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius)
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
if (radius > 0 && radius !== cameraState.radius) {
// if radius = 0, NaNs happen
cameraState.radius = Math.max(radius, 0.01)
cameraState.radius = Math.max(radius, 0.01);
}
}
}
if (Object.keys(cameraState).length > 0) camera.setState(cameraState)
if (Object.keys(cameraState).length > 0) camera.setState(cameraState);
if (props.camera?.helper) drawPass.setProps({ cameraHelper: props.camera.helper })
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
if (props.camera?.helper) drawPass.setProps({ cameraHelper: props.camera.helper });
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs;
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground;
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
if (props.multiSample) multiSample.setProps(props.multiSample)
if (props.renderer) renderer.setProps(props.renderer)
if (props.trackball) controls.setProps(props.trackball)
if (props.debug) debugHelper.setProps(props.debug)
if (props.postprocessing) postprocessing.setProps(props.postprocessing);
if (props.multiSample) multiSample.setProps(props.multiSample);
if (props.renderer) renderer.setProps(props.renderer);
if (props.trackball) controls.setProps(props.trackball);
if (props.debug) debugHelper.setProps(props.debug);
requestDraw(true)
requestDraw(true);
},
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props);
},
get props() {
const radius = scene.boundingSphere.radius > 0
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
: 0
: 0;
return {
camera: {
@@ -538,43 +564,43 @@ namespace Canvas3D {
renderer: { ...renderer.props },
trackball: { ...controls.props },
debug: { ...debugHelper.props }
}
};
},
get input() {
return input
return input;
},
get stats() {
return renderer.stats
return renderer.stats;
},
get interaction() {
return interactionHelper.events
return interactionHelper.events;
},
dispose: () => {
contextRestoredSub.unsubscribe()
contextRestoredSub.unsubscribe();
scene.clear()
debugHelper.clear()
input.dispose()
controls.dispose()
renderer.dispose()
interactionHelper.dispose()
scene.clear();
debugHelper.clear();
input.dispose();
controls.dispose();
renderer.dispose();
interactionHelper.dispose();
}
}
};
function handleResize() {
width = gl.drawingBufferWidth
height = gl.drawingBufferHeight
width = gl.drawingBufferWidth;
height = gl.drawingBufferHeight;
renderer.setViewport(0, 0, width, height)
Viewport.set(camera.viewport, 0, 0, width, height)
Viewport.set(controls.viewport, 0, 0, width, height)
renderer.setViewport(0, 0, width, height);
Viewport.set(camera.viewport, 0, 0, width, height);
Viewport.set(controls.viewport, 0, 0, width, height);
drawPass.setSize(width, height)
pickPass.setSize(width, height)
postprocessing.setSize(width, height)
multiSample.setSize(width, height)
drawPass.setSize(width, height);
pickPass.setSize(width, height);
postprocessing.setSize(width, height);
multiSample.setSize(width, height);
requestDraw(true)
requestDraw(true);
}
}
}

View File

@@ -16,9 +16,9 @@ import { Camera } from '../camera';
import { absMax } from '../../mol-math/misc';
import { Binding } from '../../mol-util/binding';
const B = ButtonsType
const M = ModifiersKeys
const Trigger = Binding.Trigger
const B = ButtonsType;
const M = ModifiersKeys;
const Trigger = Binding.Trigger;
export const DefaultTrackballBindings = {
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate', 'Drag using ${triggers}'),
@@ -31,7 +31,7 @@ export const DefaultTrackballBindings = {
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom', 'Scroll using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'),
scrollFocusZoom: Binding.Empty,
}
};
export const TrackballControlsParams = {
noScroll: PD.Boolean(true, { isHidden: true }),
@@ -50,10 +50,10 @@ export const TrackballControlsParams = {
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true })
}
};
export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
export { TrackballControls }
export { TrackballControls };
interface TrackballControls {
viewport: Viewport
@@ -66,48 +66,48 @@ interface TrackballControls {
}
namespace TrackballControls {
export function create(input: InputObserver, camera: Camera, props: Partial<TrackballControlsProps> = {}): TrackballControls {
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props }
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props };
const viewport = Viewport()
const viewport = Viewport();
let disposed = false
let disposed = false;
const dragSub = input.drag.subscribe(onDrag)
const interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd)
const wheelSub = input.wheel.subscribe(onWheel)
const pinchSub = input.pinch.subscribe(onPinch)
const dragSub = input.drag.subscribe(onDrag);
const interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd);
const wheelSub = input.wheel.subscribe(onWheel);
const pinchSub = input.pinch.subscribe(onPinch);
let _isInteracting = false;
// For internal use
const lastPosition = Vec3()
const lastPosition = Vec3();
const _eye = Vec3()
const _eye = Vec3();
const _rotPrev = Vec2()
const _rotCurr = Vec2()
const _rotLastAxis = Vec3()
let _rotLastAngle = 0
const _rotPrev = Vec2();
const _rotCurr = Vec2();
const _rotLastAxis = Vec3();
let _rotLastAngle = 0;
const _zRotPrev = Vec2()
const _zRotCurr = Vec2()
let _zRotLastAngle = 0
const _zRotPrev = Vec2();
const _zRotCurr = Vec2();
let _zRotLastAngle = 0;
const _zoomStart = Vec2()
const _zoomEnd = Vec2()
const _zoomStart = Vec2();
const _zoomEnd = Vec2();
const _focusStart = Vec2()
const _focusEnd = Vec2()
const _focusStart = Vec2();
const _focusEnd = Vec2();
const _panStart = Vec2()
const _panEnd = Vec2()
const _panStart = Vec2();
const _panEnd = Vec2();
// Initial values for reseting
const target0 = Vec3.clone(camera.target)
const position0 = Vec3.clone(camera.position)
const up0 = Vec3.clone(camera.up)
const target0 = Vec3.clone(camera.target);
const position0 = Vec3.clone(camera.position);
const up0 = Vec3.clone(camera.up);
const mouseOnScreenVec2 = Vec2()
const mouseOnScreenVec2 = Vec2();
function getMouseOnScreen(pageX: number, pageY: number) {
return Vec2.set(
mouseOnScreenVec2,
@@ -116,7 +116,7 @@ namespace TrackballControls {
);
}
const mouseOnCircleVec2 = Vec2()
const mouseOnCircleVec2 = Vec2();
function getMouseOnCircle(pageX: number, pageY: number) {
return Vec2.set(
mouseOnCircleVec2,
@@ -125,125 +125,125 @@ namespace TrackballControls {
);
}
const rotAxis = Vec3()
const rotQuat = Quat()
const rotEyeDir = Vec3()
const rotObjUpDir = Vec3()
const rotObjSideDir = Vec3()
const rotMoveDir = Vec3()
const rotAxis = Vec3();
const rotQuat = Quat();
const rotEyeDir = Vec3();
const rotObjUpDir = Vec3();
const rotObjSideDir = Vec3();
const rotMoveDir = Vec3();
function rotateCamera() {
const dx = _rotCurr[0] - _rotPrev[0]
const dy = _rotCurr[1] - _rotPrev[1]
const dx = _rotCurr[0] - _rotPrev[0];
const dy = _rotCurr[1] - _rotPrev[1];
Vec3.set(rotMoveDir, dx, dy, 0);
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed;
if (angle) {
Vec3.sub(_eye, camera.position, camera.target)
Vec3.sub(_eye, camera.position, camera.target);
Vec3.normalize(rotEyeDir, _eye)
Vec3.normalize(rotObjUpDir, camera.up)
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir))
Vec3.normalize(rotEyeDir, _eye);
Vec3.normalize(rotObjUpDir, camera.up);
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir));
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy)
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx)
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy);
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx);
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir)
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye))
Quat.setAxisAngle(rotQuat, rotAxis, angle)
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir);
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye));
Quat.setAxisAngle(rotQuat, rotAxis, angle);
Vec3.transformQuat(_eye, _eye, rotQuat)
Vec3.transformQuat(camera.up, camera.up, rotQuat)
Vec3.transformQuat(_eye, _eye, rotQuat);
Vec3.transformQuat(camera.up, camera.up, rotQuat);
Vec3.copy(_rotLastAxis, rotAxis)
Vec3.copy(_rotLastAxis, rotAxis);
_rotLastAngle = angle;
} else if (!p.staticMoving && _rotLastAngle) {
_rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle)
Vec3.sub(_eye, camera.position, camera.target);
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle);
Vec3.transformQuat(_eye, _eye, rotQuat)
Vec3.transformQuat(camera.up, camera.up, rotQuat)
Vec3.transformQuat(_eye, _eye, rotQuat);
Vec3.transformQuat(camera.up, camera.up, rotQuat);
}
Vec2.copy(_rotPrev, _rotCurr)
Vec2.copy(_rotPrev, _rotCurr);
}
const zRotQuat = Quat()
const zRotQuat = Quat();
function zRotateCamera() {
const dx = _zRotCurr[0] - _zRotPrev[0]
const dy = _zRotCurr[1] - _zRotPrev[1]
const angle = p.rotateSpeed * (-dx + dy) * -0.05
const dx = _zRotCurr[0] - _zRotPrev[0];
const dy = _zRotCurr[1] - _zRotPrev[1];
const angle = p.rotateSpeed * (-dx + dy) * -0.05;
if (angle) {
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(zRotQuat, _eye, angle)
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
Vec3.sub(_eye, camera.position, camera.target);
Quat.setAxisAngle(zRotQuat, _eye, angle);
Vec3.transformQuat(camera.up, camera.up, zRotQuat);
_zRotLastAngle = angle;
} else if (!p.staticMoving && _zRotLastAngle) {
_zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle)
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
Vec3.sub(_eye, camera.position, camera.target);
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle);
Vec3.transformQuat(camera.up, camera.up, zRotQuat);
}
Vec2.copy(_zRotPrev, _zRotCurr)
Vec2.copy(_zRotPrev, _zRotCurr);
}
function zoomCamera() {
const factor = 1.0 + (_zoomEnd[1] - _zoomStart[1]) * p.zoomSpeed
const factor = 1.0 + (_zoomEnd[1] - _zoomStart[1]) * p.zoomSpeed;
if (factor !== 1.0 && factor > 0.0) {
Vec3.scale(_eye, _eye, factor)
Vec3.scale(_eye, _eye, factor);
}
if (p.staticMoving) {
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_zoomStart, _zoomEnd);
} else {
_zoomStart[1] += (_zoomEnd[1] - _zoomStart[1]) * p.dynamicDampingFactor
_zoomStart[1] += (_zoomEnd[1] - _zoomStart[1]) * p.dynamicDampingFactor;
}
}
function focusCamera() {
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed;
if (factor !== 0.0) {
const radius = Math.max(1, camera.state.radius + camera.state.radius * factor)
camera.setState({ radius })
const radius = Math.max(1, camera.state.radius + camera.state.radius * factor);
camera.setState({ radius });
}
if (p.staticMoving) {
Vec2.copy(_focusStart, _focusEnd)
Vec2.copy(_focusStart, _focusEnd);
} else {
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor;
}
}
const panMouseChange = Vec2()
const panObjUp = Vec3()
const panOffset = Vec3()
const panMouseChange = Vec2();
const panObjUp = Vec3();
const panOffset = Vec3();
function panCamera() {
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart)
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart);
if (Vec2.squaredMagnitude(panMouseChange)) {
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed)
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed);
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up)
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0])
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up);
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0]);
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1])
Vec3.add(panOffset, panOffset, panObjUp)
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1]);
Vec3.add(panOffset, panOffset, panObjUp);
Vec3.add(camera.position, camera.position, panOffset)
Vec3.add(camera.target, camera.target, panOffset)
Vec3.add(camera.position, camera.position, panOffset);
Vec3.add(camera.target, camera.target, panOffset);
if (p.staticMoving) {
Vec2.copy(_panStart, _panEnd)
Vec2.copy(_panStart, _panEnd);
} else {
Vec2.sub(panMouseChange, _panEnd, _panStart)
Vec2.scale(panMouseChange, panMouseChange, p.dynamicDampingFactor)
Vec2.add(_panStart, _panStart, panMouseChange)
Vec2.sub(panMouseChange, _panEnd, _panStart);
Vec2.scale(panMouseChange, panMouseChange, p.dynamicDampingFactor);
Vec2.add(_panStart, _panStart, panMouseChange);
}
}
}
@@ -253,19 +253,19 @@ namespace TrackballControls {
* and not too large compared to `camera.state.radiusMax`
*/
function checkDistances() {
const maxDistance = Math.min(Math.max(camera.state.radiusMax * 1000, 0.01), p.maxDistance)
const maxDistance = Math.min(Math.max(camera.state.radiusMax * 1000, 0.01), p.maxDistance);
if (Vec3.squaredMagnitude(_eye) > maxDistance * maxDistance) {
Vec3.setMagnitude(_eye, _eye, maxDistance)
Vec3.add(camera.position, camera.target, _eye)
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_focusStart, _focusEnd)
Vec3.setMagnitude(_eye, _eye, maxDistance);
Vec3.add(camera.position, camera.target, _eye);
Vec2.copy(_zoomStart, _zoomEnd);
Vec2.copy(_focusStart, _focusEnd);
}
if (Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) {
Vec3.setMagnitude(_eye, _eye, p.minDistance)
Vec3.add(camera.position, camera.target, _eye)
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_focusStart, _focusEnd)
Vec3.setMagnitude(_eye, _eye, p.minDistance);
Vec3.add(camera.position, camera.target, _eye);
Vec2.copy(_zoomStart, _zoomEnd);
Vec2.copy(_focusStart, _focusEnd);
}
}
@@ -275,19 +275,19 @@ namespace TrackballControls {
if (lastUpdated === t) return;
if (p.spin) spin(t - lastUpdated);
Vec3.sub(_eye, camera.position, camera.target)
Vec3.sub(_eye, camera.position, camera.target);
rotateCamera()
zRotateCamera()
zoomCamera()
focusCamera()
panCamera()
rotateCamera();
zRotateCamera();
zoomCamera();
focusCamera();
panCamera();
Vec3.add(camera.position, camera.target, _eye)
checkDistances()
Vec3.add(camera.position, camera.target, _eye);
checkDistances();
if (Vec3.squaredDistance(lastPosition, camera.position) > EPSILON) {
Vec3.copy(lastPosition, camera.position)
Vec3.copy(lastPosition, camera.position);
}
lastUpdated = t;
@@ -295,12 +295,12 @@ namespace TrackballControls {
/** Reset object's vectors and the target vector to their initial values */
function reset() {
Vec3.copy(camera.target, target0)
Vec3.copy(camera.position, position0)
Vec3.copy(camera.up, up0)
Vec3.copy(camera.target, target0);
Vec3.copy(camera.position, position0);
Vec3.copy(camera.up, up0);
Vec3.sub(_eye, camera.position, camera.target)
Vec3.copy(lastPosition, camera.position)
Vec3.sub(_eye, camera.position, camera.target);
Vec3.copy(lastPosition, camera.position);
}
// listeners
@@ -308,48 +308,48 @@ namespace TrackballControls {
function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
_isInteracting = true;
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers)
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers)
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers)
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers)
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers)
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers)
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers);
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers);
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers);
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers);
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers);
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers);
getMouseOnCircle(pageX, pageY)
getMouseOnScreen(pageX, pageY)
getMouseOnCircle(pageX, pageY);
getMouseOnScreen(pageX, pageY);
if (isStart) {
if (dragRotate) {
Vec2.copy(_rotCurr, mouseOnCircleVec2)
Vec2.copy(_rotPrev, _rotCurr)
Vec2.copy(_rotCurr, mouseOnCircleVec2);
Vec2.copy(_rotPrev, _rotCurr);
}
if (dragRotateZ) {
Vec2.copy(_zRotCurr, mouseOnCircleVec2)
Vec2.copy(_zRotPrev, _zRotCurr)
Vec2.copy(_zRotCurr, mouseOnCircleVec2);
Vec2.copy(_zRotPrev, _zRotCurr);
}
if (dragZoom || dragFocusZoom) {
Vec2.copy(_zoomStart, mouseOnScreenVec2)
Vec2.copy(_zoomEnd, _zoomStart)
Vec2.copy(_zoomStart, mouseOnScreenVec2);
Vec2.copy(_zoomEnd, _zoomStart);
}
if (dragFocus) {
Vec2.copy(_focusStart, mouseOnScreenVec2)
Vec2.copy(_focusEnd, _focusStart)
Vec2.copy(_focusStart, mouseOnScreenVec2);
Vec2.copy(_focusEnd, _focusStart);
}
if (dragPan) {
Vec2.copy(_panStart, mouseOnScreenVec2)
Vec2.copy(_panEnd, _panStart)
Vec2.copy(_panStart, mouseOnScreenVec2);
Vec2.copy(_panEnd, _panStart);
}
}
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2)
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2)
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2)
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2);
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2);
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2);
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2);
if (dragFocusZoom) {
const dist = Vec3.distance(camera.state.position, camera.state.target);
camera.setState({ radius: dist / 5 })
camera.setState({ radius: dist / 5 });
}
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2);
}
function onInteractionEnd() {
@@ -357,30 +357,30 @@ namespace TrackballControls {
}
function onWheel({ dx, dy, dz, buttons, modifiers }: WheelInput) {
const delta = absMax(dx, dy, dz)
const delta = absMax(dx, dy, dz);
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_zoomEnd[1] += delta * 0.0001
_zoomEnd[1] += delta * 0.0001;
}
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
_focusEnd[1] += delta * 0.0001
_focusEnd[1] += delta * 0.0001;
}
}
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_isInteracting = true;
_zoomEnd[1] += (fraction - 1) * 0.1
_zoomEnd[1] += (fraction - 1) * 0.1;
}
}
function dispose() {
if (disposed) return
disposed = true
if (disposed) return;
disposed = true;
dragSub.unsubscribe()
wheelSub.unsubscribe()
pinchSub.unsubscribe()
interactionEndSub.unsubscribe()
dragSub.unsubscribe();
wheelSub.unsubscribe();
pinchSub.unsubscribe();
interactionEndSub.unsubscribe();
}
const _spinSpeed = Vec2.create(0.005, 0);
@@ -396,14 +396,14 @@ namespace TrackballControls {
return {
viewport,
get props() { return p as Readonly<TrackballControlsProps> },
get props() { return p as Readonly<TrackballControlsProps>; },
setProps: (props: Partial<TrackballControlsProps>) => {
Object.assign(p, props)
Object.assign(p, props);
},
update,
reset,
dispose
}
};
}
}

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createRenderObject, GraphicsRenderObject, getNextMaterialId } from '../../mol-gl/render-object'
import { createRenderObject, GraphicsRenderObject, getNextMaterialId } from '../../mol-gl/render-object';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
@@ -24,7 +24,7 @@ export const DebugHelperParams = {
visibleSceneBoundingSpheres: PD.Boolean(false, { description: 'Show visible scene bounding spheres.' }),
objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of visible render objects.' }),
instanceBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of visible instances.' }),
}
};
export type DebugHelperParams = typeof DebugHelperParams
export type DebugHelperProps = PD.Values<DebugHelperParams>
@@ -41,24 +41,24 @@ export class BoundingSphereHelper {
private visibleSceneData: BoundingSphereData | undefined
constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
this.scene = Scene.create(ctx)
this.parent = parent
this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props }
this.scene = Scene.create(ctx);
this.parent = parent;
this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props };
}
update() {
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.lightgrey, sceneMaterialId)
if (newSceneData) this.sceneData = newSceneData
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.lightgrey, sceneMaterialId);
if (newSceneData) this.sceneData = newSceneData;
const newVisibleSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphereVisible, this.visibleSceneData, ColorNames.black, visibleSceneMaterialId)
if (newVisibleSceneData) this.visibleSceneData = newVisibleSceneData
const newVisibleSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphereVisible, this.visibleSceneData, ColorNames.black, visibleSceneMaterialId);
if (newVisibleSceneData) this.visibleSceneData = newVisibleSceneData;
this.parent.forEach((r, ro) => {
const objectData = this.objectsData.get(ro)
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato, objectMaterialId)
if (newObjectData) this.objectsData.set(ro, newObjectData)
const objectData = this.objectsData.get(ro);
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato, objectMaterialId);
if (newObjectData) this.objectsData.set(ro, newObjectData);
const instanceData = this.instancesData.get(ro)
const instanceData = this.instancesData.get(ro);
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, instanceMaterialId, {
aTransform: ro.values.aTransform,
matrix: ro.values.matrix,
@@ -67,97 +67,97 @@ export class BoundingSphereHelper {
uInstanceCount: ro.values.uInstanceCount,
instanceCount: ro.values.instanceCount,
aInstance: ro.values.aInstance,
})
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
})
});
if (newInstanceData) this.instancesData.set(ro, newInstanceData);
});
this.objectsData.forEach((objectData, ro) => {
if (!this.parent.has(ro)) {
this.scene.remove(objectData.renderObject)
this.objectsData.delete(ro)
this.scene.remove(objectData.renderObject);
this.objectsData.delete(ro);
}
})
});
this.instancesData.forEach((instanceData, ro) => {
if (!this.parent.has(ro)) {
this.scene.remove(instanceData.renderObject)
this.instancesData.delete(ro)
this.scene.remove(instanceData.renderObject);
this.instancesData.delete(ro);
}
})
});
this.scene.update(void 0, false)
this.scene.commit()
this.scene.update(void 0, false);
this.scene.commit();
}
syncVisibility() {
if (this.sceneData) {
this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres
this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres;
}
if (this.visibleSceneData) {
this.visibleSceneData.renderObject.state.visible = this._props.visibleSceneBoundingSpheres
this.visibleSceneData.renderObject.state.visible = this._props.visibleSceneBoundingSpheres;
}
this.parent.forEach((_, ro) => {
const objectData = this.objectsData.get(ro)
if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres
const objectData = this.objectsData.get(ro);
if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres;
const instanceData = this.instancesData.get(ro)
if (instanceData) instanceData.renderObject.state.visible = ro.state.visible && this._props.instanceBoundingSpheres
})
const instanceData = this.instancesData.get(ro);
if (instanceData) instanceData.renderObject.state.visible = ro.state.visible && this._props.instanceBoundingSpheres;
});
}
clear() {
this.sceneData = undefined
this.objectsData.clear()
this.scene.clear()
this.sceneData = undefined;
this.objectsData.clear();
this.scene.clear();
}
get isEnabled() {
return (
this._props.sceneBoundingSpheres || this._props.visibleSceneBoundingSpheres ||
this._props.objectBoundingSpheres || this._props.instanceBoundingSpheres
)
);
}
get props() { return this._props as Readonly<DebugHelperProps> }
get props() { return this._props as Readonly<DebugHelperProps>; }
setProps (props: Partial<DebugHelperProps>) {
Object.assign(this._props, props)
if (this.isEnabled) this.update()
Object.assign(this._props, props);
if (this.isEnabled) this.update();
}
}
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, materialId: number, transform?: TransformData) {
if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) {
const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh)
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, materialId, transform)
const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh);
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, materialId, transform);
if (data) {
ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(mesh))
ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(mesh));
} else {
scene.add(renderObject)
scene.add(renderObject);
}
return { boundingSphere: Sphere3D.clone(boundingSphere), renderObject, mesh }
return { boundingSphere: Sphere3D.clone(boundingSphere), renderObject, mesh };
}
}
function createBoundingSphereMesh(boundingSphere: Sphere3D, mesh?: Mesh) {
const detail = 2
const vertexCount = sphereVertexCount(detail)
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
const detail = 2;
const vertexCount = sphereVertexCount(detail);
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh);
if (boundingSphere.radius) {
addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail);
if (Sphere3D.hasExtrema(boundingSphere)) {
for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0)
for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0);
}
}
return MeshBuilder.getMesh(builderState)
return MeshBuilder.getMesh(builderState);
}
const sceneMaterialId = getNextMaterialId()
const visibleSceneMaterialId = getNextMaterialId()
const objectMaterialId = getNextMaterialId()
const instanceMaterialId = getNextMaterialId()
const sceneMaterialId = getNextMaterialId();
const visibleSceneMaterialId = getNextMaterialId();
const objectMaterialId = getNextMaterialId();
const instanceMaterialId = getNextMaterialId();
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, materialId)
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform);
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, materialId);
}

View File

@@ -31,7 +31,7 @@ const AxesParams = {
colorY: PD.Color(ColorNames.green, { isEssential: true }),
colorZ: PD.Color(ColorNames.blue, { isEssential: true }),
scale: PD.Numeric(0.33, { min: 0.1, max: 2, step: 0.1 }, { isEssential: true }),
}
};
type AxesParams = typeof AxesParams
type AxesProps = PD.Values<AxesParams>
@@ -40,7 +40,7 @@ export const CameraHelperParams = {
on: PD.Group(AxesParams),
off: PD.Group({})
}, { cycle: true, description: 'Show camera orientation axes' }),
}
};
export type CameraHelperParams = typeof CameraHelperParams
export type CameraHelperProps = PD.Values<CameraHelperParams>
@@ -54,122 +54,122 @@ export class CameraHelper {
private renderObject: GraphicsRenderObject | undefined
constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
this.scene = Scene.create(webgl)
this.scene = Scene.create(webgl);
this.camera = new Camera()
Vec3.set(this.camera.up, 0, 1, 0)
Vec3.set(this.camera.target, 0, 0, 0)
this.camera = new Camera();
Vec3.set(this.camera.up, 0, 1, 0);
Vec3.set(this.camera.target, 0, 0, 0);
this.setProps(props)
this.setProps(props);
}
setProps(props: Partial<CameraHelperProps>) {
this.props = produce(this.props, p => {
if (props.axes !== undefined) {
p.axes.name = props.axes.name
p.axes.name = props.axes.name;
if (props.axes.name === 'on') {
this.scene.clear()
const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio }
this.renderObject = createAxesRenderObject(params)
this.scene.add(this.renderObject)
this.scene.commit()
this.scene.clear();
const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio };
this.renderObject = createAxesRenderObject(params);
this.scene.add(this.renderObject);
this.scene.commit();
Vec3.set(this.camera.position, 0, 0, params.scale * 200)
Mat4.lookAt(this.camera.view, this.camera.position, this.camera.target, this.camera.up)
Vec3.set(this.camera.position, 0, 0, params.scale * 200);
Mat4.lookAt(this.camera.view, this.camera.position, this.camera.target, this.camera.up);
p.axes.params = { ...props.axes.params }
p.axes.params = { ...props.axes.params };
}
}
})
});
}
get isEnabled() {
return this.props.axes.name === 'on'
return this.props.axes.name === 'on';
}
update(camera: Camera) {
if (!this.renderObject) return
if (!this.renderObject) return;
updateCamera(this.camera, camera.viewport)
updateCamera(this.camera, camera.viewport);
const m = this.renderObject.values.aTransform.ref.value as unknown as Mat4
Mat4.extractRotation(m, camera.view)
const m = this.renderObject.values.aTransform.ref.value as unknown as Mat4;
Mat4.extractRotation(m, camera.view);
const r = this.renderObject.values.boundingSphere.ref.value.radius
const r = this.renderObject.values.boundingSphere.ref.value.radius;
Mat4.setTranslation(m, Vec3.create(
-camera.viewport.width / 2 + r,
-camera.viewport.height / 2 + r,
0
))
));
ValueCell.update(this.renderObject.values.aTransform, this.renderObject.values.aTransform.ref.value)
this.scene.update([this.renderObject], true)
ValueCell.update(this.renderObject.values.aTransform, this.renderObject.values.aTransform.ref.value);
this.scene.update([this.renderObject], true);
}
}
function updateCamera(camera: Camera, viewport: Viewport) {
const { near, far } = camera
const { near, far } = camera;
const fullLeft = -(viewport.width - viewport.x) / 2
const fullRight = (viewport.width - viewport.x) / 2
const fullTop = (viewport.height - viewport.y) / 2
const fullBottom = -(viewport.height - viewport.y) / 2
const fullLeft = -(viewport.width - viewport.x) / 2;
const fullRight = (viewport.width - viewport.x) / 2;
const fullTop = (viewport.height - viewport.y) / 2;
const fullBottom = -(viewport.height - viewport.y) / 2;
const dx = (fullRight - fullLeft) / 2
const dy = (fullTop - fullBottom) / 2
const cx = (fullRight + fullLeft) / 2
const cy = (fullTop + fullBottom) / 2
const dx = (fullRight - fullLeft) / 2;
const dy = (fullTop - fullBottom) / 2;
const cx = (fullRight + fullLeft) / 2;
const cy = (fullTop + fullBottom) / 2;
const left = cx - dx
const right = cx + dx
const top = cy + dy
const bottom = cy - dy
const left = cx - dx;
const right = cx + dx;
const top = cy + dy;
const bottom = cy - dy;
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
Mat4.ortho(camera.projection, left, right, top, bottom, near, far);
}
function createAxesMesh(scale: number, mesh?: Mesh) {
const state = MeshBuilder.createState(512, 256, mesh)
const radius = 0.05 * scale
const x = Vec3.scale(Vec3(), Vec3.unitX, scale)
const y = Vec3.scale(Vec3(), Vec3.unitY, scale)
const z = Vec3.scale(Vec3(), Vec3.unitZ, scale)
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 }
const state = MeshBuilder.createState(512, 256, mesh);
const radius = 0.05 * scale;
const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
state.currentGroup = 0
addSphere(state, Vec3.origin, radius, 2)
state.currentGroup = 0;
addSphere(state, Vec3.origin, radius, 2);
state.currentGroup = 1
addSphere(state, x, radius, 2)
addCylinder(state, Vec3.origin, x, 1, cylinderProps)
state.currentGroup = 1;
addSphere(state, x, radius, 2);
addCylinder(state, Vec3.origin, x, 1, cylinderProps);
state.currentGroup = 2
addSphere(state, y, radius, 2)
addCylinder(state, Vec3.origin, y, 1, cylinderProps)
state.currentGroup = 2;
addSphere(state, y, radius, 2);
addCylinder(state, Vec3.origin, y, 1, cylinderProps);
state.currentGroup = 3
addSphere(state, z, radius, 2)
addCylinder(state, Vec3.origin, z, 1, cylinderProps)
state.currentGroup = 3;
addSphere(state, z, radius, 2);
addCylinder(state, Vec3.origin, z, 1, cylinderProps);
return MeshBuilder.getMesh(state)
return MeshBuilder.getMesh(state);
}
function getAxesShape(props: AxesProps, shape?: Shape<Mesh>) {
const scale = 100 * props.scale
const mesh = createAxesMesh(scale, shape?.geometry)
mesh.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale + scale / 4))
const scale = 100 * props.scale;
const mesh = createAxesMesh(scale, shape?.geometry);
mesh.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale + scale / 4));
const getColor = (groupId: number) => {
switch (groupId) {
case 1: return props.colorX
case 2: return props.colorY
case 3: return props.colorZ
default: return ColorNames.grey
case 1: return props.colorX;
case 2: return props.colorY;
case 3: return props.colorZ;
default: return ColorNames.grey;
}
}
return Shape.create('axes', {}, mesh, getColor, () => 1, () => '')
};
return Shape.create('axes', {}, mesh, getColor, () => 1, () => '');
}
function createAxesRenderObject(props: AxesProps) {
const shape = getAxesShape(props)
return Shape.createRenderObject(shape, props)
const shape = getAxesShape(props);
return Shape.createRenderObject(shape, props);
}

View File

@@ -16,7 +16,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
export const DrawPassParams = {
cameraHelper: PD.Group(CameraHelperParams)
}
};
export const DefaultDrawPassProps = PD.getDefaultValues(DrawPassParams);
export type DrawPassProps = PD.Values<typeof DrawPassParams>
@@ -30,74 +30,74 @@ export class DrawPass {
private depthTarget: RenderTarget | null
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, props: Partial<DrawPassProps> = {}) {
const { gl, extensions, resources } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
this.colorTarget = webgl.createRenderTarget(width, height)
this.packedDepth = !extensions.depthTexture
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest')
const { gl, extensions, resources } = webgl;
const width = gl.drawingBufferWidth;
const height = gl.drawingBufferHeight;
this.colorTarget = webgl.createRenderTarget(width, height);
this.packedDepth = !extensions.depthTexture;
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest');
if (!this.packedDepth) {
this.depthTexture.define(width, height)
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
this.depthTexture.define(width, height);
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
}
const p = { ...DefaultDrawPassProps, ...props }
const p = { ...DefaultDrawPassProps, ...props };
this.cameraHelper = new CameraHelper(webgl, p.cameraHelper);
}
setSize(width: number, height: number) {
this.colorTarget.setSize(width, height)
this.colorTarget.setSize(width, height);
if (this.depthTarget) {
this.depthTarget.setSize(width, height)
this.depthTarget.setSize(width, height);
} else {
this.depthTexture.define(width, height)
this.depthTexture.define(width, height);
}
}
setProps(props: Partial<DrawPassProps>) {
if (props.cameraHelper) this.cameraHelper.setProps(props.cameraHelper)
if (props.cameraHelper) this.cameraHelper.setProps(props.cameraHelper);
}
get props(): DrawPassProps {
return {
cameraHelper: { ...this.cameraHelper.props }
}
};
}
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { webgl, renderer, colorTarget, depthTarget } = this
const { webgl, renderer, colorTarget, depthTarget } = this;
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
webgl.unbindFramebuffer();
} else {
colorTarget.bind()
colorTarget.bind();
if (!this.packedDepth) {
// TODO unlcear why it is not enough to call `attachFramebuffer` in `Texture.reset`
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
}
}
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight())
this.renderInternal('color', transparentBackground)
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight());
this.renderInternal('color', transparentBackground);
// do a depth pass if not rendering to drawing buffer and
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (!toDrawingBuffer && depthTarget) {
depthTarget.bind()
this.renderInternal('depth', transparentBackground)
depthTarget.bind();
this.renderInternal('depth', transparentBackground);
}
}
private renderInternal(variant: 'color' | 'depth', transparentBackground: boolean) {
const { renderer, scene, camera, debugHelper, cameraHelper } = this
renderer.render(scene, camera, variant, true, transparentBackground)
const { renderer, scene, camera, debugHelper, cameraHelper } = this;
renderer.render(scene, camera, variant, true, transparentBackground);
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, camera, variant, false, transparentBackground)
debugHelper.syncVisibility();
renderer.render(debugHelper.scene, camera, variant, false, transparentBackground);
}
if (cameraHelper.isEnabled) {
cameraHelper.update(camera)
renderer.render(cameraHelper.scene, cameraHelper.camera, variant, false, transparentBackground)
cameraHelper.update(camera);
renderer.render(cameraHelper.scene, cameraHelper.camera, variant, false, transparentBackground);
}
}
}

View File

@@ -10,9 +10,9 @@ import Renderer from '../../mol-gl/renderer';
import Scene from '../../mol-gl/scene';
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { DrawPass, DrawPassParams } from './draw'
import { PostprocessingPass, PostprocessingParams } from './postprocessing'
import { MultiSamplePass, MultiSampleParams } from './multi-sample'
import { DrawPass, DrawPassParams } from './draw';
import { PostprocessingPass, PostprocessingParams } from './postprocessing';
import { MultiSamplePass, MultiSampleParams } from './multi-sample';
import { Camera } from '../camera';
import { Viewport } from '../camera/util';
@@ -21,7 +21,7 @@ export const ImageParams = {
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
drawPass: PD.Group(DrawPassParams),
}
};
export type ImageProps = PD.Values<typeof ImageParams>
export class ImagePass {
@@ -31,43 +31,43 @@ export class ImagePass {
private _transparentBackground = false
private _colorTarget: RenderTarget
get colorTarget() { return this._colorTarget }
get colorTarget() { return this._colorTarget; }
readonly drawPass: DrawPass
private readonly postprocessing: PostprocessingPass
private readonly multiSample: MultiSamplePass
get width() { return this._width }
get height() { return this._height }
get width() { return this._width; }
get height() { return this._height; }
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
const p = { ...PD.getDefaultValues(ImageParams), ...props }
const p = { ...PD.getDefaultValues(ImageParams), ...props };
this._transparentBackground = p.transparentBackground
this._transparentBackground = p.transparentBackground;
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, p.drawPass)
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, p.drawPass);
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing);
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample);
this.setSize(this._width, this._height)
this.setSize(this._width, this._height);
}
setSize(width: number, height: number) {
if (width === this._width && height === this._height) return
if (width === this._width && height === this._height) return;
this._width = width
this._height = height
this._width = width;
this._height = height;
this.drawPass.setSize(width, height)
this.postprocessing.setSize(width, height)
this.multiSample.setSize(width, height)
this.drawPass.setSize(width, height);
this.postprocessing.setSize(width, height);
this.multiSample.setSize(width, height);
}
setProps(props: Partial<ImageProps> = {}) {
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
if (props.multiSample) this.multiSample.setProps(props.multiSample)
if (props.drawPass) this.drawPass.setProps(props.drawPass)
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground;
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing);
if (props.multiSample) this.multiSample.setProps(props.multiSample);
if (props.drawPass) this.drawPass.setProps(props.drawPass);
}
get props(): ImageProps {
@@ -76,34 +76,34 @@ export class ImagePass {
postprocessing: this.postprocessing.props,
multiSample: this.multiSample.props,
drawPass: this.drawPass.props
}
};
}
render() {
Camera.copySnapshot(this._camera.state, this.camera.state)
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
this._camera.update()
Camera.copySnapshot(this._camera.state, this.camera.state);
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height);
this._camera.update();
this.renderer.setViewport(0, 0, this._width, this._height);
if (this.multiSample.enabled) {
this.multiSample.render(false, this._transparentBackground)
this._colorTarget = this.multiSample.colorTarget
this.multiSample.render(false, this._transparentBackground);
this._colorTarget = this.multiSample.colorTarget;
} else {
this.drawPass.render(false, this._transparentBackground)
this.drawPass.render(false, this._transparentBackground);
if (this.postprocessing.enabled) {
this.postprocessing.render(false)
this._colorTarget = this.postprocessing.target
this.postprocessing.render(false);
this._colorTarget = this.postprocessing.target;
} else {
this._colorTarget = this.drawPass.colorTarget
this._colorTarget = this.drawPass.colorTarget;
}
}
}
getImageData(width: number, height: number) {
this.setSize(width, height)
this.render()
const pd = this.colorTarget.getPixelData()
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
this.setSize(width, height);
this.render();
const pd = this.colorTarget.getPixelData();
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height);
}
}

View File

@@ -19,15 +19,15 @@ import { Camera } from '../../mol-canvas3d/camera';
import { PostprocessingPass } from './postprocessing';
import { DrawPass } from './draw';
import quad_vert from '../../mol-gl/shader/quad.vert'
import compose_frag from '../../mol-gl/shader/compose.frag'
import quad_vert from '../../mol-gl/shader/quad.vert';
import compose_frag from '../../mol-gl/shader/compose.frag';
const ComposeSchema = {
...QuadSchema,
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
uWeight: UniformSpec('f'),
}
};
type ComposeRenderable = ComputeRenderable<Values<typeof ComposeSchema>>
@@ -37,19 +37,19 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
tColor: ValueCell.create(colorTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
uWeight: ValueCell.create(1.0),
}
};
const schema = { ...ComposeSchema }
const shaderCode = ShaderCode('compose', quad_vert, compose_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
const schema = { ...ComposeSchema };
const shaderCode = ShaderCode('compose', quad_vert, compose_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
return createComputeRenderable(renderItem, values)
return createComputeRenderable(renderItem, values);
}
export const MultiSampleParams = {
mode: PD.Select('off', [['off', 'Off'], ['on', 'On'], ['temporal', 'Temporal']]),
sampleLevel: PD.Numeric(2, { min: 0, max: 5, step: 1 }),
}
};
export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
export class MultiSamplePass {
@@ -65,229 +65,229 @@ export class MultiSamplePass {
private lastRenderTime = 0
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
const { gl } = webgl
this.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props }
const { gl } = webgl;
this.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight);
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight);
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight);
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture);
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props };
}
get enabled() {
if (this.props.mode === 'temporal') {
if (this.currentTime - this.lastRenderTime > 200) {
return this.sampleIndex !== -1
return this.sampleIndex !== -1;
} else {
this.sampleIndex = 0
return false
this.sampleIndex = 0;
return false;
}
} else if (this.props.mode === 'on') {
return true
return true;
} else {
return false
return false;
}
}
update(changed: boolean, currentTime: number) {
if (changed) this.lastRenderTime = currentTime
this.currentTime = currentTime
if (changed) this.lastRenderTime = currentTime;
this.currentTime = currentTime;
}
setSize(width: number, height: number) {
this.colorTarget.setSize(width, height)
this.composeTarget.setSize(width, height)
this.holdTarget.setSize(width, height)
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
this.colorTarget.setSize(width, height);
this.composeTarget.setSize(width, height);
this.holdTarget.setSize(width, height);
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height));
}
setProps(props: Partial<MultiSampleProps>) {
if (props.mode !== undefined) this.props.mode = props.mode
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
if (props.mode !== undefined) this.props.mode = props.mode;
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel;
}
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
if (this.props.mode === 'temporal') {
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground);
} else {
this.renderMultiSample(toDrawingBuffer, transparentBackground)
this.renderMultiSample(toDrawingBuffer, transparentBackground);
}
}
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
const { gl, state } = webgl
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this;
const { gl, state } = webgl;
// based on the Multisample Anti-Aliasing Render Pass
// contributed to three.js by bhouston / http://clara.io/
//
// This manual approach to MSAA re-renders the scene once for
// each sample with camera jitter and accumulates the results.
const offsetList = JitterVectors[ Math.max(0, Math.min(this.props.sampleLevel, 5)) ]
const offsetList = JitterVectors[ Math.max(0, Math.min(this.props.sampleLevel, 5)) ];
const baseSampleWeight = 1.0 / offsetList.length
const roundingRange = 1 / 32
const baseSampleWeight = 1.0 / offsetList.length;
const roundingRange = 1 / 32;
camera.viewOffset.enabled = true
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
compose.update()
camera.viewOffset.enabled = true;
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture);
compose.update();
const width = drawPass.colorTarget.getWidth()
const height = drawPass.colorTarget.getHeight()
const width = drawPass.colorTarget.getWidth();
const height = drawPass.colorTarget.getHeight();
// render the scene multiple times, each slightly jitter offset
// from the last and accumulate the results.
for (let i = 0; i < offsetList.length; ++i) {
const offset = offsetList[i]
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
camera.update()
const offset = offsetList[i];
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height);
camera.update();
// the theory is that equal weights for each sample lead to an accumulation of rounding
// errors. The following equation varies the sampleWeight per sample so that it is uniformly
// distributed across a range of values whose rounding errors cancel each other out.
const uniformCenteredDistribution = -0.5 + (i + 0.5) / offsetList.length
const sampleWeight = baseSampleWeight + roundingRange * uniformCenteredDistribution
ValueCell.update(compose.values.uWeight, sampleWeight)
const uniformCenteredDistribution = -0.5 + (i + 0.5) / offsetList.length;
const sampleWeight = baseSampleWeight + roundingRange * uniformCenteredDistribution;
ValueCell.update(compose.values.uWeight, sampleWeight);
// render scene and optionally postprocess
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
drawPass.render(false, transparentBackground);
if (postprocessing.enabled) postprocessing.render(false);
// compose rendered scene with compose target
composeTarget.bind()
gl.viewport(0, 0, width, height)
state.enable(gl.BLEND)
state.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD)
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE)
state.disable(gl.DEPTH_TEST)
state.disable(gl.SCISSOR_TEST)
state.depthMask(false)
composeTarget.bind();
gl.viewport(0, 0, width, height);
state.enable(gl.BLEND);
state.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.depthMask(false);
if (i === 0) {
state.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
compose.render()
compose.render();
}
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, composeTarget.texture)
compose.update()
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, composeTarget.texture);
compose.update();
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
webgl.unbindFramebuffer();
} else {
this.colorTarget.bind()
this.colorTarget.bind();
}
gl.viewport(0, 0, width, height)
state.disable(gl.BLEND)
compose.render()
gl.viewport(0, 0, width, height);
state.disable(gl.BLEND);
compose.render();
camera.viewOffset.enabled = false
camera.update()
camera.viewOffset.enabled = false;
camera.update();
}
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
const { gl, state } = webgl
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this;
const { gl, state } = webgl;
// based on the Multisample Anti-Aliasing Render Pass
// contributed to three.js by bhouston / http://clara.io/
//
// This manual approach to MSAA re-renders the scene once for
// each sample with camera jitter and accumulates the results.
const offsetList = JitterVectors[ Math.max(0, Math.min(this.props.sampleLevel, 5)) ]
const offsetList = JitterVectors[ Math.max(0, Math.min(this.props.sampleLevel, 5)) ];
if (this.sampleIndex === -1) return
if (this.sampleIndex === -1) return;
if (this.sampleIndex >= offsetList.length) {
this.sampleIndex = -1
return
this.sampleIndex = -1;
return;
}
const i = this.sampleIndex
const i = this.sampleIndex;
if (i === 0) {
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
compose.update()
drawPass.render(false, transparentBackground);
if (postprocessing.enabled) postprocessing.render(false);
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture);
compose.update();
holdTarget.bind()
state.disable(gl.BLEND)
compose.render()
holdTarget.bind();
state.disable(gl.BLEND);
compose.render();
}
const sampleWeight = 1.0 / offsetList.length
const sampleWeight = 1.0 / offsetList.length;
camera.viewOffset.enabled = true
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
ValueCell.update(compose.values.uWeight, sampleWeight)
compose.update()
camera.viewOffset.enabled = true;
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture);
ValueCell.update(compose.values.uWeight, sampleWeight);
compose.update();
const width = drawPass.colorTarget.getWidth()
const height = drawPass.colorTarget.getHeight()
const width = drawPass.colorTarget.getWidth();
const height = drawPass.colorTarget.getHeight();
// render the scene multiple times, each slightly jitter offset
// from the last and accumulate the results.
const numSamplesPerFrame = Math.pow(2, this.props.sampleLevel)
const numSamplesPerFrame = Math.pow(2, this.props.sampleLevel);
for (let i = 0; i < numSamplesPerFrame; ++i) {
const offset = offsetList[this.sampleIndex]
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
camera.update()
const offset = offsetList[this.sampleIndex];
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height);
camera.update();
// render scene and optionally postprocess
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
drawPass.render(false, transparentBackground);
if (postprocessing.enabled) postprocessing.render(false);
// compose rendered scene with compose target
composeTarget.bind()
state.enable(gl.BLEND)
state.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD)
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE)
state.disable(gl.DEPTH_TEST)
state.disable(gl.SCISSOR_TEST)
state.depthMask(false)
composeTarget.bind();
state.enable(gl.BLEND);
state.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.depthMask(false);
if (this.sampleIndex === 0) {
state.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
compose.render()
compose.render();
this.sampleIndex += 1
if (this.sampleIndex >= offsetList.length ) break
this.sampleIndex += 1;
if (this.sampleIndex >= offsetList.length ) break;
}
const accumulationWeight = this.sampleIndex * sampleWeight
const accumulationWeight = this.sampleIndex * sampleWeight;
if (accumulationWeight > 0) {
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, composeTarget.texture)
compose.update()
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, composeTarget.texture);
compose.update();
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
webgl.unbindFramebuffer();
} else {
this.colorTarget.bind()
this.colorTarget.bind();
}
gl.viewport(0, 0, width, height)
state.disable(gl.BLEND)
compose.render()
gl.viewport(0, 0, width, height);
state.disable(gl.BLEND);
compose.render();
}
if (accumulationWeight < 1.0) {
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
ValueCell.update(compose.values.tColor, holdTarget.texture)
compose.update()
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight);
ValueCell.update(compose.values.tColor, holdTarget.texture);
compose.update();
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
webgl.unbindFramebuffer();
} else {
this.colorTarget.bind()
this.colorTarget.bind();
}
gl.viewport(0, 0, width, height)
if (accumulationWeight === 0) state.disable(gl.BLEND)
else state.enable(gl.BLEND)
compose.render()
gl.viewport(0, 0, width, height);
if (accumulationWeight === 0) state.disable(gl.BLEND);
else state.enable(gl.BLEND);
compose.render();
}
camera.viewOffset.enabled = false
camera.update()
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1
camera.viewOffset.enabled = false;
camera.update();
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1;
}
}
@@ -321,12 +321,12 @@ const JitterVectors = [
[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
]
]
];
JitterVectors.forEach(offsetList => {
offsetList.forEach(offset => {
// 0.0625 = 1 / 16
offset[0] *= 0.0625
offset[1] *= 0.0625
})
})
offset[0] *= 0.0625;
offset[1] *= 0.0625;
});
});

View File

@@ -28,44 +28,44 @@ export class PickPass {
private pickHeight: number
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
const { gl } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
const { gl } = webgl;
const width = gl.drawingBufferWidth;
const height = gl.drawingBufferHeight;
this.pickScale = pickBaseScale / webgl.pixelRatio
this.pickWidth = Math.round(width * this.pickScale)
this.pickHeight = Math.round(height * this.pickScale)
this.pickScale = pickBaseScale / webgl.pixelRatio;
this.pickWidth = Math.round(width * this.pickScale);
this.pickHeight = Math.round(height * this.pickScale);
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
this.setupBuffers()
this.setupBuffers();
}
private setupBuffers() {
const bufferSize = this.pickWidth * this.pickHeight * 4
const bufferSize = this.pickWidth * this.pickHeight * 4;
if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
this.objectBuffer = new Uint8Array(bufferSize)
this.instanceBuffer = new Uint8Array(bufferSize)
this.groupBuffer = new Uint8Array(bufferSize)
this.objectBuffer = new Uint8Array(bufferSize);
this.instanceBuffer = new Uint8Array(bufferSize);
this.groupBuffer = new Uint8Array(bufferSize);
}
}
setSize(width: number, height: number) {
this.pickScale = this.pickBaseScale / this.webgl.pixelRatio
this.pickWidth = Math.round(width * this.pickScale)
this.pickHeight = Math.round(height * this.pickScale)
this.pickScale = this.pickBaseScale / this.webgl.pixelRatio;
this.pickWidth = Math.round(width * this.pickScale);
this.pickHeight = Math.round(height * this.pickScale);
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight)
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight)
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight)
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
this.setupBuffers()
this.setupBuffers();
}
render() {
const { renderer, scene, camera } = this
const { renderer, scene, camera } = this;
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
this.objectPickTarget.bind();
renderer.render(scene, camera, 'pickObject', true, false);
@@ -74,53 +74,53 @@ export class PickPass {
this.groupPickTarget.bind();
renderer.render(scene, camera, 'pickGroup', true, false);
this.pickDirty = false
this.pickDirty = false;
}
private syncBuffers() {
const { webgl } = this
const { webgl } = this;
this.objectPickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer)
this.objectPickTarget.bind();
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer);
this.instancePickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer)
this.instancePickTarget.bind();
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer);
this.groupPickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer)
this.groupPickTarget.bind();
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer);
}
private getId(x: number, y: number, buffer: Uint8Array) {
const idx = (y * this.pickWidth + x) * 4
return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2])
const idx = (y * this.pickWidth + x) * 4;
return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2]);
}
identify(x: number, y: number): PickingId | undefined {
const { webgl, pickScale } = this
if (webgl.isContextLost) return
const { webgl, pickScale } = this;
if (webgl.isContextLost) return;
const { gl } = webgl
const { gl } = webgl;
if (this.pickDirty) {
this.render()
this.syncBuffers()
this.render();
this.syncBuffers();
}
x *= webgl.pixelRatio
y *= webgl.pixelRatio
y = gl.drawingBufferHeight - y // flip y
x *= webgl.pixelRatio;
y *= webgl.pixelRatio;
y = gl.drawingBufferHeight - y; // flip y
const xp = Math.round(x * pickScale)
const yp = Math.round(y * pickScale)
const xp = Math.round(x * pickScale);
const yp = Math.round(y * pickScale);
const objectId = this.getId(xp, yp, this.objectBuffer)
if (objectId === -1) return
const objectId = this.getId(xp, yp, this.objectBuffer);
if (objectId === -1) return;
const instanceId = this.getId(xp, yp, this.instanceBuffer)
if (instanceId === -1) return
const instanceId = this.getId(xp, yp, this.instanceBuffer);
if (instanceId === -1) return;
const groupId = this.getId(xp, yp, this.groupBuffer)
if (groupId === -1) return
const groupId = this.getId(xp, yp, this.groupBuffer);
if (groupId === -1) return;
return { objectId, instanceId, groupId }
return { objectId, instanceId, groupId };
}
}

View File

@@ -19,8 +19,8 @@ import { DrawPass } from './draw';
import { Camera } from '../../mol-canvas3d/camera';
import { produce } from 'immer';
import quad_vert from '../../mol-gl/shader/quad.vert'
import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag'
import quad_vert from '../../mol-gl/shader/quad.vert';
import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag';
const PostprocessingSchema = {
...QuadSchema,
@@ -45,7 +45,7 @@ const PostprocessingSchema = {
uOutlineThreshold: UniformSpec('f'),
dPackedDepth: DefineSpec('boolean'),
}
};
export const PostprocessingParams = {
occlusion: PD.MappedStatic('off', {
@@ -63,13 +63,13 @@ export const PostprocessingParams = {
}),
off: PD.Group({})
}, { cycle: true, description: 'Draw outline around 3D objects' })
}
};
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, packedDepth: boolean, props: Partial<PostprocessingProps>): PostprocessingRenderable {
const p = { ...PD.getDefaultValues(PostprocessingParams), ...props }
const p = { ...PD.getDefaultValues(PostprocessingParams), ...props };
const values: Values<typeof PostprocessingSchema> = {
...QuadValues,
tColor: ValueCell.create(colorTexture),
@@ -93,13 +93,13 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uOutlineThreshold: ValueCell.create(p.outline.name === 'on' ? p.outline.params.threshold : 0.8),
dPackedDepth: ValueCell.create(packedDepth),
}
};
const schema = { ...PostprocessingSchema }
const shaderCode = ShaderCode('postprocessing', quad_vert, postprocessing_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
const schema = { ...PostprocessingSchema };
const shaderCode = ShaderCode('postprocessing', quad_vert, postprocessing_frag);
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
return createComputeRenderable(renderItem, values)
return createComputeRenderable(renderItem, values);
}
export class PostprocessingPass {
@@ -108,67 +108,67 @@ export class PostprocessingPass {
renderable: PostprocessingRenderable
constructor(private webgl: WebGLContext, private camera: Camera, drawPass: DrawPass, props: Partial<PostprocessingProps>) {
const { gl } = webgl
this.target = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props }
const { colorTarget, depthTexture, packedDepth } = drawPass
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props)
const { gl } = webgl;
this.target = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight);
this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props };
const { colorTarget, depthTexture, packedDepth } = drawPass;
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props);
}
get enabled() {
return this.props.occlusion.name === 'on' || this.props.outline.name === 'on'
return this.props.occlusion.name === 'on' || this.props.outline.name === 'on';
}
setSize(width: number, height: number) {
this.target.setSize(width, height)
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height))
this.target.setSize(width, height);
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
}
setProps(props: Partial<PostprocessingProps>) {
this.props = produce(this.props, p => {
if (props.occlusion !== undefined) {
p.occlusion.name = props.occlusion.name
ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, props.occlusion.name === 'on')
p.occlusion.name = props.occlusion.name;
ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, props.occlusion.name === 'on');
if (props.occlusion.name === 'on') {
p.occlusion.params = { ...props.occlusion.params }
ValueCell.updateIfChanged(this.renderable.values.dOcclusionKernelSize, props.occlusion.params.kernelSize)
ValueCell.updateIfChanged(this.renderable.values.uOcclusionBias, props.occlusion.params.bias)
ValueCell.updateIfChanged(this.renderable.values.uOcclusionRadius, props.occlusion.params.radius)
p.occlusion.params = { ...props.occlusion.params };
ValueCell.updateIfChanged(this.renderable.values.dOcclusionKernelSize, props.occlusion.params.kernelSize);
ValueCell.updateIfChanged(this.renderable.values.uOcclusionBias, props.occlusion.params.bias);
ValueCell.updateIfChanged(this.renderable.values.uOcclusionRadius, props.occlusion.params.radius);
}
}
if (props.outline !== undefined) {
p.outline.name = props.outline.name
ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, props.outline.name === 'on')
p.outline.name = props.outline.name;
ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, props.outline.name === 'on');
if (props.outline.name === 'on') {
p.outline.params = { ...props.outline.params }
ValueCell.updateIfChanged(this.renderable.values.uOutlineScale, props.outline.params.scale)
ValueCell.updateIfChanged(this.renderable.values.uOutlineThreshold, props.outline.params.threshold)
p.outline.params = { ...props.outline.params };
ValueCell.updateIfChanged(this.renderable.values.uOutlineScale, props.outline.params.scale);
ValueCell.updateIfChanged(this.renderable.values.uOutlineThreshold, props.outline.params.threshold);
}
}
})
});
this.renderable.update()
this.renderable.update();
}
render(toDrawingBuffer: boolean) {
ValueCell.update(this.renderable.values.uFar, this.camera.far)
ValueCell.update(this.renderable.values.uNear, this.camera.near)
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
ValueCell.update(this.renderable.values.uFar, this.camera.far);
ValueCell.update(this.renderable.values.uNear, this.camera.near);
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar);
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear);
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0);
const { gl, state } = this.webgl
const { gl, state } = this.webgl;
if (toDrawingBuffer) {
this.webgl.unbindFramebuffer()
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.webgl.unbindFramebuffer();
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
} else {
this.target.bind()
this.target.bind();
}
state.disable(gl.SCISSOR_TEST)
state.disable(gl.BLEND)
state.disable(gl.DEPTH_TEST)
state.depthMask(false)
this.renderable.render()
state.disable(gl.SCISSOR_TEST);
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
this.renderable.render();
}
}

View File

@@ -6,40 +6,40 @@
/** Set canvas size taking `devicePixelRatio` into account */
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
canvas.width = Math.round(window.devicePixelRatio * width)
canvas.height = Math.round(window.devicePixelRatio * height)
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
canvas.width = Math.round(window.devicePixelRatio * width);
canvas.height = Math.round(window.devicePixelRatio * height);
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` });
}
/** Resize canvas to container element taking `devicePixelRatio` into account */
export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
let width = window.innerWidth
let height = window.innerHeight
let width = window.innerWidth;
let height = window.innerHeight;
if (container !== document.body) {
let bounds = container.getBoundingClientRect()
width = bounds.right - bounds.left
height = bounds.bottom - bounds.top
let bounds = container.getBoundingClientRect();
width = bounds.right - bounds.left;
height = bounds.bottom - bounds.top;
}
setCanvasSize(canvas, width, height)
setCanvasSize(canvas, width, height);
}
function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?: string, quality?: any) {
const bin = atob(canvas.toDataURL(type, quality).split(',')[1])
const len = bin.length
const len32 = len >> 2
const a8 = new Uint8Array(len)
const a32 = new Uint32Array(a8.buffer, 0, len32)
const bin = atob(canvas.toDataURL(type, quality).split(',')[1]);
const len = bin.length;
const len32 = len >> 2;
const a8 = new Uint8Array(len);
const a32 = new Uint32Array(a8.buffer, 0, len32);
let j = 0
let j = 0;
for (let i = 0; i < len32; ++i) {
a32[i] = bin.charCodeAt(j++) |
bin.charCodeAt(j++) << 8 |
bin.charCodeAt(j++) << 16 |
bin.charCodeAt(j++) << 24
bin.charCodeAt(j++) << 24;
}
let tailLength = len & 3;
while (tailLength--) a8[j] = bin.charCodeAt(j++)
while (tailLength--) a8[j] = bin.charCodeAt(j++);
callback(new Blob([a8], { type: type || 'image/png' }));
}
@@ -47,14 +47,14 @@ function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?:
export async function canvasToBlob(canvas: HTMLCanvasElement, type?: string, quality?: any): Promise<Blob> {
return new Promise((resolve, reject) => {
const callback = (blob: Blob | null) => {
if (blob) resolve(blob)
else reject('no blob returned')
}
if (blob) resolve(blob);
else reject('no blob returned');
};
if (!HTMLCanvasElement.prototype.toBlob) {
_canvasToBlob(canvas, callback, type, quality)
_canvasToBlob(canvas, callback, type, quality);
} else {
canvas.toBlob(callback, type, quality)
canvas.toBlob(callback, type, quality);
}
})
});
}

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { EquivalenceClasses } from '../util'
import { EquivalenceClasses } from '../util';
describe('equiv-classes', () => {
it('integer mod classes', () => {

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Iterator from '../iterator'
import Iterator from '../iterator';
function iteratorToArray<T>(it: Iterator<T>): T[] {
const ret = [];

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