Compare commits

...

736 Commits

Author SHA1 Message Date
David Sehnal
16d5c07224 0.7.0-dev.11 2020-04-26 18:04:53 +02:00
David Sehnal
2392bfb579 ParamDefinition.mergeParam fix 2020-04-26 17:54:42 +02:00
David Sehnal
b4036f576c proteopedia-wrapper tweaks 2020-04-26 13:19:10 +02:00
Alexander Rose
690d6812dc cellpack: simple cache to avoid parsing trajectories more than once 2020-04-26 00:56:03 -07:00
Alexander Rose
a44aa02f13 cellpack: support for loading zip files containing model.json and ingredients 2020-04-25 23:50:27 -07:00
Alexander Rose
65ddd6d68a Mol file description and extension tweaks 2020-04-25 15:56:13 -07:00
Alexander Rose
754025b3b1 fix bond label between identically named elements/atoms 2020-04-25 13:11:17 -07:00
Alexander Rose
f0649c5aa3 improved structure selection query labels 2020-04-25 12:51:08 -07:00
Alexander Rose
6df045211c fixed atomicDetail repr preset 2020-04-25 12:50:32 -07:00
Alexander Rose
8a00540de0 0.7.0-dev.10 2020-04-24 19:12:20 -07:00
Alexander Rose
0d78905686 icon css tweak 2020-04-24 19:11:24 -07:00
Alexander Rose
6edab203c2 0.7.0-dev.9 2020-04-24 18:49:20 -07:00
Alexander Rose
0abfdb5ee3 material icon css tweaks 2020-04-24 18:48:17 -07:00
Alexander Rose
88369158c9 0.7.0-dev.8 2020-04-24 18:01:32 -07:00
Alexander Rose
8926575283 larger volume-cell bounding-sphere radius 2020-04-24 18:00:00 -07:00
Alexander Rose
15b0288ce4 selection ui tooltip tweaks 2020-04-24 17:59:39 -07:00
Alexander Rose
28853ec19d 0.7.0-dev.7 2020-04-24 17:30:18 -07:00
Alexander Rose
e3480a076a tooltip tweaks 2020-04-24 16:28:32 -07:00
Alexander Rose
abf6452124 package updates 2020-04-24 16:20:05 -07:00
Alexander Rose
2cdd811dd3 repr preset tweaks- higher opacity since not double-side by default anymore- add snfg3d symbols for auto all-atom preset 2020-04-24 16:12:36 -07:00
Alexander Rose
be6fea39bf fog and image shader tweaks 2020-04-24 15:58:08 -07:00
Alexander Rose
ed1bc2cd07 don't set doubleSided=true when alpha<1 in getQualityProps 2020-04-24 15:57:41 -07:00
Alexander Rose
28d3d5861a add missing , BaseGeometry.CustomQualityParamInfo 2020-04-24 15:56:46 -07:00
Alexander Rose
95d3ef491f structure selection query improvements
- moved out of selection.tsx
- added queries for entities (based on entity description)
2020-04-24 13:21:30 -07:00
Alexander Rose
5c37ddfc6d add names for all elements 2020-04-24 13:20:00 -07:00
Alexander Rose
4d9e2d9c91 support title in ControlGroup and ActionMenu 2020-04-24 13:19:41 -07:00
Alexander Rose
0b1c18913d added MolScript.core.list.equal and MolScript.structureQuery.atomProperty.macromolecular.entityDescription 2020-04-24 12:35:08 -07:00
Alexander Rose
c8c2355d3e 0.7.0-dev.6 2020-04-23 20:43:57 -07:00
Alexander Rose
3d1366024d added more structure selection queries
- whole residues
- non-standard residues from current structures
- elements from current structures
2020-04-23 20:43:00 -07:00
Alexander Rose
f6964d2a66 added Structure.uniqueElementSymbols 2020-04-23 20:42:15 -07:00
Alexander Rose
de60f70af5 relax isApplicable validation-report checks 2020-04-23 16:33:41 -07:00
Alexander Rose
8f2e619162 fix assembly symmetry cage alignement 2020-04-23 16:32:44 -07:00
Alexander Rose
fbcef01c55 0.7.0-dev.5 2020-04-23 14:40:43 -07:00
Alexander Rose
641e0639d4 fix filehandle usage in server/ 2020-04-23 14:39:32 -07:00
Alexander Rose
5048573976 0.7.0-dev.4 2020-04-23 12:53:29 -07:00
Alexander Rose
78e4d8536d tweaked ligand definition 2020-04-23 12:51:52 -07:00
Alexander Rose
a01d088205 allow spaces in download id list 2020-04-23 12:48:23 -07:00
Alexander Rose
4b1d1a045d cube tweaks
- swapped visuals colors
- add orbitals flag to header
- TODO add format data to volumes as in structures
2020-04-23 11:55:58 -07:00
Alexander Rose
e2857d00b4 volume label improvements
- add cell value to loci label
- add file name to volume data objects
2020-04-23 10:51:07 -07:00
Alexander Rose
a55a71d31a bounding sphere calc for volume cell loci 2020-04-23 10:04:43 -07:00
David Sehnal
acf793f112 Tensor.Space.getCoords 2020-04-23 12:21:25 +02:00
Alexander Rose
2d58ea28ea Merge branch 'master' of https://github.com/molstar/molstar 2020-04-22 19:07:18 -07:00
Alexander Rose
b21de78eb5 camera focus for non-structure loci 2020-04-22 19:06:40 -07:00
Alexander Rose
376d4b4ee1 volume improvements and slice repr 2020-04-22 19:06:18 -07:00
Alexander Rose
e39304c7cf add dataOffset method Tensor.Space 2020-04-22 16:02:05 -07:00
Alexander Rose
7cba9cda0c support flipY for textures 2020-04-22 16:01:29 -07:00
Alexander Rose
04c690e8f9 add .writeDepth to renderable state
- renders transparent with writeDepth=true before writeDepth=true
2020-04-22 16:00:38 -07:00
David Sehnal
170d0fbc9d fix VolumeData.One matrix, removed pesky console.logs 2020-04-23 00:28:21 +02:00
David Sehnal
40d632a7b1 Volume streaming UI: toggle channel visibility 2020-04-23 00:17:10 +02:00
David Sehnal
2e754d23f4 0.7.0-dev.3 2020-04-22 18:59:20 +02:00
David Sehnal
5639a4b37c build: plugin version that does not rely on external variables 2020-04-22 14:45:57 +02:00
David Sehnal
8c959f8a60 QueryRuntimeTable.removeSymbol/CustomProp 2020-04-22 14:07:11 +02:00
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
Alexander Rose
4a7413a8d9 0.6.2 2020-04-06 12:43:22 -07:00
Alexander Rose
593b34bfe3 added binding to add loci to current focus 2020-04-06 12:40:24 -07:00
Alexander Rose
691b1721b2 tweaked select mode icon 2020-04-06 11:27:09 -07:00
David Sehnal
b8133b4300 Canvas3dInteractionHelper.maxFps = 30
- makes the interaction feel more responsive
2020-04-06 18:42:10 +02:00
David Sehnal
0c459b98db mol-plugin: added PluginContext.state.updateBehavior 2020-04-06 17:33:22 +02:00
David Sehnal
060725642f mol-plugin: state tree UI fix + remove global current object 2020-04-06 16:45:28 +02:00
David Sehnal
eaf28bf016 fix PDBeStructureQualityReport.autoAttach 2020-04-06 16:23:11 +02:00
David Sehnal
ed6084c40b mol-plugin-ui: optimize state tree rendering 2020-04-06 16:18:23 +02:00
David Sehnal
e56063b065 mol-plugin: selection mode 2020-04-06 15:08:00 +02:00
Alexander Rose
f8ddfb1638 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-05 20:59:49 -07:00
Alexander Rose
900f2e1f76 wip, selection viewport controls
- refactored selection controls as components
- initial css, buggy
2020-04-05 20:59:17 -07:00
David Sehnal
00c9e05a65 mol-plugin-ui: use componentDidUpdated instead of getDerivedStateFromProps where appropriate 2020-04-06 03:20:54 +02:00
Alexander Rose
ecaab51315 support update of props.initiallyCollapsed in CollapsableControls 2020-04-05 17:17:48 -07:00
Alexander Rose
2a4d45714c added npm script and webpack config for viewer debugging
- generates sourcemaps that work with vscode/chrome debugging
2020-04-05 14:18:17 -07:00
Alexander Rose
6e13ef5cbc use webpack.DefinePlugin.runtimeValue for __VERSION__ 2020-04-03 18:54:04 -07:00
Alexander Rose
f48e2ab238 0.6.1 2020-04-03 17:52:16 -07:00
Alexander Rose
0e34d976c0 use version from package.json for plugin 2020-04-03 17:50:50 -07:00
Alexander Rose
2d6cd4c6da package updates 2020-04-03 17:42:59 -07:00
Alexander Rose
4a4b3ef5b4 fix whitespace linting issues 2020-04-03 17:29:59 -07:00
Alexander Rose
67e6167b55 removed orientation from measurements for now
- too limited and confusing
2020-04-03 17:26:32 -07:00
Alexander Rose
79b33bfbd8 selection and focus tweaks
- support highlight for current selection UI
2020-04-03 17:25:36 -07:00
Alexander Rose
f5fc96ee3b more label tweaks
- show entity in locilabel-provider for single-entity loci
- better category handling in focus ui
2020-04-03 16:49:41 -07:00
Alexander Rose
691571ec39 better handling of labels from multiple structures
- intruduced group hash
2020-04-03 15:48:05 -07:00
Alexander Rose
f2b20a646e cellpack tweaks 2020-04-03 12:37:35 -07:00
Alexander Rose
bef9ff86d2 remove console.log 2020-04-03 12:20:07 -07:00
Alexander Rose
ff3da5f2db refactored cellpack loader
- properly handle coloring
- support hiv_lipids loading
2020-04-03 12:09:35 -07:00
Alexander Rose
129727d5d1 calculateTransformBoundingSphere: only use extrema if there are not too many transforms 2020-04-03 12:06:51 -07:00
Alexander Rose
6e26b4d5f9 fix SequenceWrapper type determination
- failed in case of Partitioned chains
2020-04-03 12:06:08 -07:00
Alexander Rose
371bb11ce8 0.6.0 2020-04-02 21:11:10 -07:00
Alexander Rose
556196ae3f wip, cellpack model loader 2020-04-02 21:09:36 -07:00
Alexander Rose
f5667411d7 check if carbs are applicable without calculating them 2020-04-02 20:56:36 -07:00
Alexander Rose
774f419a53 add traceOnly support to element-sphere visual 2020-04-02 20:55:59 -07:00
Alexander Rose
9fa5d40306 improved labels in measurement representations 2020-04-02 18:48:38 -07:00
Alexander Rose
ba89d5ec1e simple PD.Mat4 2020-04-02 17:47:32 -07:00
Alexander Rose
b7fa577d9b improved validation-report and assembly-symmetry presets 2020-04-02 17:21:05 -07:00
David Sehnal
949425d14d mol-plugin-ui: fixed crash caused by invalid state during updates 2020-04-03 00:40:48 +02:00
Alexander Rose
2cb0d63750 added more descriptions and titles
- added description prop to action-item
- use for presets and selection queries
2020-04-02 13:16:14 -07:00
Alexander Rose
038aa47578 update assembly-symmetry UI when repr is added 2020-04-02 12:01:42 -07:00
Alexander Rose
c833598c26 improved computing of covalent bonds with hydrogen
- added more X-H pair thresholds
- removed maxCovalentHydrogenBondingLength param
2020-04-02 11:20:24 -07:00
Alexander Rose
489c52361a measurement label tweaks 2020-04-02 10:18:01 -07:00
David Sehnal
9edf41a2f2 model-server: requestTimeout config option 2020-04-02 16:08:05 +02:00
David Sehnal
5bcca99f60 mol-plugin: support request body in Download(Blob) transforms 2020-04-02 14:33:29 +02:00
David Sehnal
1b0a310fc7 mol-state & volume streaming updates
- error cells are no longer considered valid update roots
- volume streaming now better handles initialization errors
2020-04-02 14:27:55 +02:00
David Sehnal
e3e7fa3040 model-server: support tar.gz output in local mode 2020-04-02 13:52:42 +02:00
David Sehnal
5297dd6f11 model-server: query many limit 2020-04-02 12:49:39 +02:00
David Sehnal
c557e93255 model-server: do not apply content-encoing: gzip to tar.gz resposes 2020-04-02 03:03:52 +02:00
Alexander Rose
ad5f0c987a 0.6.0-dev.11 2020-04-01 17:32:01 -07:00
Alexander Rose
30aa6d035d tweaked volume streaming labels 2020-04-01 17:29:15 -07:00
Alexander Rose
b198289fc4 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-01 17:25:25 -07:00
Alexander Rose
51e45085f1 package updates 2020-04-01 17:22:47 -07:00
Alexander Rose
97ebb2dccc fixed labels for MultiChain units 2020-04-01 17:07:53 -07:00
David Sehnal
29d28bd3f4 UI tweaks 2020-04-02 01:40:23 +02:00
David Sehnal
09c5c040f2 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-02 01:26:39 +02:00
David Sehnal
6e60a2f9dc model-server: query-many result as tar.gz 2020-04-02 01:26:30 +02:00
Alexander Rose
5d36108113 added title tooltip to CollapsableControls 2020-04-01 16:16:37 -07:00
Alexander Rose
f4a423ebe5 show mssage when enabling volume streaming fails 2020-04-01 16:13:21 -07:00
Alexander Rose
9942fbe549 added EmdbHeaderServer plugin config item 2020-04-01 14:46:56 -07:00
Alexander Rose
dbde7521e4 improved volume streaming type determination 2020-04-01 14:34:38 -07:00
David Sehnal
6eee3e8368 model-server: refactored result writer 2020-04-01 22:25:25 +02:00
Alexander Rose
cc0ccd7830 made cameraHelper part of drawPass, screenshot improvements 2020-04-01 12:41:33 -07:00
Alexander Rose
7ad25249a9 removed async renderObject creation in cameraHelper 2020-04-01 12:40:29 -07:00
Alexander Rose
687c7d54ff Shape: extracted helper methods 2020-04-01 12:39:16 -07:00
Alexander Rose
d6fff1ffdf Merge pull request #27 from ValentaTomas/ValentaTomas-readme-typo-fix
Fix README typos
2020-04-01 10:54:42 -07:00
David Sehnal
20a8b8892e colored icons for structure tools 2020-04-01 18:50:37 +02:00
David Sehnal
5c09ecc98d StateTree facelift 2020-04-01 18:04:22 +02:00
ValentaTomas
19539a2c9d Fix README typos 2020-04-01 14:54:53 +02:00
David Sehnal
5c093c7f22 mol-canvas-3d: camera transition tweak 2020-04-01 14:30:51 +02:00
Alexander Rose
c43ed90607 improve volume streaming method determination
- take content_type of related entry into account
2020-03-31 22:26:05 -07:00
Alexander Rose
a85242d9c5 improved focus entry computation
- handle branched
- handle Unit.Trait.MultiChain
2020-03-31 18:43:54 -07:00
Alexander Rose
722d2a9c6c include selected values in sequence panel select titles 2020-03-31 17:31:56 -07:00
Alexander Rose
f7d65ff52c avoid breaking long residue names in sequence panel 2020-03-31 16:52:24 -07:00
Alexander Rose
7bee2be12d added traceOnly param to gaussian-surface
- avoids creating trace structure selection with slow labeling, stats and bundle params
2020-03-31 15:31:41 -07:00
Alexander Rose
326649d4f5 fix broken getLoci in Representation.createMulti 2020-03-31 15:28:56 -07:00
David Sehnal
6aba5df55d Merge branch 'master' of https://github.com/molstar/molstar 2020-03-31 21:08:34 +02:00
David Sehnal
57bbcf9425 fixed issue that prevented delayed isBusy and enabled it again
- lesson learned: do not raise events inside property getters
2020-03-31 21:08:15 +02:00
Alexander Rose
08ba34d607 0.6.0-dev.10 2020-03-31 11:15:57 -07:00
David Sehnal
4c122227a7 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-31 20:10:31 +02:00
David Sehnal
e14afb4dad reverted delayed isBusy
- made CSS/code tweaks in the process
2020-03-31 20:10:22 +02:00
Alexander Rose
472def49f6 0.6.0-dev.9 2020-03-31 10:17:54 -07:00
Alexander Rose
90549893e3 added pdbx_entity_instance_feature to mmcif schema 2020-03-31 09:49:18 -07:00
Alexander Rose
73a8e45202 added missing pdbx_branch_scheme fields to mmcif schema 2020-03-31 09:26:29 -07:00
David Sehnal
35df55cb4f CSS & structure type auto-apply tweaks 2020-03-31 17:19:03 +02:00
David Sehnal
c2028d20a8 mol-plugin: do not disable UI for fast updates 2020-03-31 16:35:45 +02:00
David Sehnal
eaffdc6a98 mol-plugin-ui: do not auto-apply symmetry structure types 2020-03-31 15:52:56 +02:00
David Sehnal
0b1e6100a9 mol-canvas-3d: fix camera transition when adding new representations 2020-03-31 15:25:00 +02:00
David Sehnal
2168905c11 StructureHierarchyManager.updateCurrent fix 2020-03-31 14:57:49 +02:00
David Sehnal
f955e6a299 mol-plugin-state: changing structure "type" recreates the subtree
* different assemblies can have different default components
* i.e. assembly 1 doesn't have carbs and asm 2 does
2020-03-31 14:55:59 +02:00
David Sehnal
98f3981e12 Fix CSS issues 2020-03-31 14:12:45 +02:00
Alexander Rose
82f94d20ea wip, cellpack file input 2020-03-31 02:04:18 -07:00
Alexander Rose
fbc6d47117 wip, cellpack loader update 2020-03-31 01:19:07 -07:00
Alexander Rose
6a2e4cf813 handle orphan models and structures in StructureHierarchyManager 2020-03-31 01:18:41 -07:00
Alexander Rose
4aabef7683 package updates 2020-03-31 00:48:40 -07:00
Alexander Rose
74ae91ee1b support initiallyCollapsed in CustomStructureControls 2020-03-31 00:24:17 -07:00
Alexander Rose
8b62766474 add data-color to ButtonProps (for color swatches) 2020-03-30 23:03:20 -07:00
Alexander Rose
2995504916 made StructureMeasurementsControls seperate panel again 2020-03-30 22:56:01 -07:00
Alexander Rose
63f6848d26 0.6.0-dev.8 2020-03-30 22:35:58 -07:00
Alexander Rose
988e429693 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-30 19:27:35 -07:00
David Sehnal
ba1c6ef046 CameraTransitionManager.apply reverse change 2020-03-31 03:49:20 +02:00
David Sehnal
7ceff92a4e fix structure focus camera animation
+ more UI code tweaks
2020-03-31 03:45:48 +02:00
Alexander Rose
1f968b2836 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-30 18:37:14 -07:00
Alexander Rose
d97d7e3b14 AssemblySymmetryControls fixes 2020-03-30 18:37:03 -07:00
Alexander Rose
5c4c4811e4 added .props() to CustomProperty.Provider 2020-03-30 18:35:30 -07:00
David Sehnal
f2a6e63a20 mol-plugin-ui: general refactoring & code improvements 2020-03-31 02:35:58 +02:00
David Sehnal
1aace4a26f mol-plugin-ui: added Button common ctrl 2020-03-31 01:43:04 +02:00
David Sehnal
b1c140d23e IconButton flex prop 2020-03-31 00:42:54 +02:00
David Sehnal
ee16212c31 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-31 00:14:29 +02:00
David Sehnal
f6d232b1c5 mol-plugin-ui: removed unused CSS 2020-03-31 00:14:13 +02:00
Alexander Rose
c8a002933e take PluginContext.spec into account for SimpleSettingsMapping.layout 2020-03-30 14:55:28 -07:00
Alexander Rose
4757ca9913 fix clashes variable in validationReportPreset 2020-03-30 14:30:00 -07:00
Alexander Rose
5264c75e37 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-30 12:29:23 -07:00
Alexander Rose
032bf44863 package updates (npm audit fix) 2020-03-30 12:02:28 -07:00
David Sehnal
2f4f5e43f3 StructureFocusManager.behaviors.current
replaces events.changed
2020-03-30 20:57:39 +02:00
Alexander Rose
7b1edcadf6 fous fixes and improvements 2020-03-30 11:53:55 -07:00
Alexander Rose
f0f74d182d updated schemas 2020-03-30 11:36:37 -07:00
Alexander Rose
59142adbbc updated packages 2020-03-30 11:26:09 -07:00
Alexander Rose
2fda8c5db1 tweaked assembly symmetry ui 2020-03-30 11:21:07 -07:00
Alexander Rose
7b5efa3e42 LociLabelManager: fold and count identical labels 2020-03-30 10:05:14 -07:00
Alexander Rose
d784d202bd volume streaming: ensure current focus is shown upon registering 2020-03-30 09:52:48 -07:00
Alexander Rose
8833474a43 structure size and quality threshold improvements 2020-03-30 09:51:04 -07:00
David Sehnal
57cbb2f8b6 StructureSourceControls: Fix crash when selecting "Symmetry (assembly)" 2020-03-28 18:01:56 +01:00
David Sehnal
b6112a914f StateTransformer.dispose
- use in StructureRepresentation3D to release custom props
2020-03-28 17:45:16 +01:00
David Sehnal
2008f8538c Various tweaks and fixes
- allow to hide expand button in Viewport
- principal axes fix for single element
- fixes to preset syncing
- CSS fixes
- picking level grouping
2020-03-28 16:46:47 +01:00
David Sehnal
09fba43a1c mol-plugin-state: representation presets "sync" instead of recreate 2020-03-28 14:00:01 +01:00
David Sehnal
b76c3613f9 Focus/Selection UI tweaks 2020-03-28 12:38:39 +01:00
Alexander Rose
cf4ddcb587 label tweaks 2020-03-27 23:42:03 -07:00
Alexander Rose
696106f48b check distance for IntraUnitClashes 2020-03-27 21:54:30 -07:00
Alexander Rose
fb286cd9cf take pixelRatio into account for camera-helper 2020-03-27 21:44:02 -07:00
Alexander Rose
5121bd700e changed assembly-symmetry preset to StructureRepresentationPresetProvider 2020-03-27 21:25:18 -07:00
Alexander Rose
6173520ad0 add validation-report preset 2020-03-27 20:09:17 -07:00
Alexander Rose
a7189232dd wip, StructureFocus controls and manager 2020-03-27 20:00:02 -07:00
Alexander Rose
4a96b45b04 added StructureSelectionQueryRegistry
- register hasClashes, isAccessible, isBuried from their custom props
2020-03-27 19:51:19 -07:00
David Sehnal
20ac549dd6 Plugin UI tweaks
- component focus ensures visibility
- moved measurements to selection section
2020-03-27 18:46:27 +01:00
David Sehnal
38be00c0b7 fix StateTreeNode display bug 2020-03-27 17:10:44 +01:00
David Sehnal
0a9bdc8cf6 model-server: query-many (wip)
+ some UI changes
2020-03-27 16:01:29 +01:00
David Sehnal
0b4318280a cosmetic UI changes 2020-03-27 12:29:51 +01:00
Alexander Rose
b1da60e1c0 wip, focus manager, focus repr refactoring 2020-03-26 18:32:21 -07:00
David Sehnal
2cc5987f9e cosmetic UI changes 2020-03-26 15:52:23 +01:00
David Sehnal
745415a1d8 rename first-model trajectory preset to default 2020-03-26 14:09:50 +01:00
David Sehnal
c80658f368 hide reprLabel in StructureComponentGroup 2020-03-26 13:15:27 +01:00
David Sehnal
033675a417 mol-plugin-ui: GenericEntry.showOnFocus option 2020-03-26 13:09:09 +01:00
David Sehnal
3dd57e9dc8 PD.ColorList refactoring 2020-03-26 12:49:31 +01:00
Alexander Rose
1e3daa6c98 fixed color-theme param getters for palette.scale.list.predefined 2020-03-25 19:44:57 -07:00
Alexander Rose
8855f51cfc Merge branch 'master' of https://github.com/molstar/molstar 2020-03-25 19:33:13 -07:00
Alexander Rose
50d8debb2b refactored assembly-symmetry
- provide a single symmetry-index, story data in AssemblySymmetryDataProvider
2020-03-25 19:32:30 -07:00
Alexander Rose
e682eb78b0 various ui tweaks 2020-03-25 19:31:39 -07:00
Alexander Rose
bd2bbf3e2d plugin: added customStructureControls renamed genericRepresentationControls 2020-03-25 19:30:46 -07:00
Alexander Rose
f38f040aea updated DefaultCellPackBaseUrl 2020-03-25 19:25:02 -07:00
David Sehnal
f912b2d802 proteopedia wrapper bugfix 2020-03-25 21:20:51 +01:00
David Sehnal
0ad03e6a0b custom interpolation list for color palette 2020-03-25 20:46:14 +01:00
Alexander Rose
160d8c529e Merge branch 'master' of https://github.com/molstar/molstar 2020-03-25 10:42:43 -07:00
David Sehnal
28edfd810c mol-model: molstar_atom_site_operator_mapping parsing 2020-03-25 18:29:10 +01:00
Alexander Rose
7b931cfb66 wip, assembly-symmetry preset 2020-03-25 10:28:55 -07:00
David Sehnal
5575c61577 mmcif export updates- rename chains with asm/symm/ncs operators- molstar_atom_site_operator_mapping category 2020-03-25 17:32:15 +01:00
Alexander Rose
8f93dce105 tweaked Structure.hasHighSymmetry 2020-03-25 08:48:26 -07:00
David Sehnal
0eb3d7226a SymmetryOperator.create refactor, added SymmetryOperator.suffix 2020-03-25 15:56:20 +01:00
Alexander Rose
a9533b666c added TrajectoryHierarchyBuilder 2020-03-24 19:23:13 -07:00
David Sehnal
6d67b4db56 update camera axes default params to make it less distracting 2020-03-24 11:46:38 +01:00
David Sehnal
1b5eff6454 ui improvements
- state tree update/apply transform
- focus label, customize radius
- StructureComponentGroup repr label
2020-03-24 11:41:30 +01:00
Alexander Rose
83fb28cc9d wip, generic representation entry 2020-03-23 19:31:07 -07:00
Alexander Rose
c39ffc0b0e fix Infinity in projection matrix
- ensure near and far are not identical
2020-03-23 19:30:18 -07:00
Alexander Rose
23892cfbdd fixed genericTarget in hierarchy-state tagMap 2020-03-23 19:05:45 -07:00
Alexander Rose
3bdabc444d camera helper param tweaks 2020-03-23 15:14:16 -07:00
Alexander Rose
c233be4467 set isoValue param as essential 2020-03-23 15:08:54 -07:00
Alexander Rose
8468f33803 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-23 14:56:55 -07:00
Alexander Rose
0e77369fdb added camera helper showing orientation axes 2020-03-23 14:55:54 -07:00
Alexander Rose
9fcc8e7977 param handling tweaks 2020-03-23 12:16:57 -07:00
David Sehnal
538371ced8 Remove StructureBuilderTags.Component
+ remove some unused code
2020-03-23 19:11:00 +01:00
Alexander Rose
380887bd22 improved trackball.focusCamera
- radius change is now relative to current radius and not fixed
2020-03-23 11:03:35 -07:00
Alexander Rose
17b4b1cb86 added visible bounding sphere to debug helper 2020-03-23 11:01:33 -07:00
Alexander Rose
3e7c358c07 improved unitcell bounding sphere (added extrema) 2020-03-23 11:00:51 -07:00
Alexander Rose
05b592a173 removed unused runTask canvas3d argument 2020-03-23 11:00:12 -07:00
Alexander Rose
e1d0515fae unified font and svg icons 2020-03-23 10:48:02 -07:00
David Sehnal
d4d3b9645e treat "current interaction" as a "structure component" 2020-03-23 18:14:01 +01:00
David Sehnal
d12d99dcfa remove unused StructureBuilderTags 2020-03-23 17:57:28 +01:00
David Sehnal
174324d21c added 'reds-darker' color list 2020-03-23 15:24:49 +01:00
David Sehnal
26156a5982 ProteopediaWrapper: snapshots 2020-03-23 15:18:40 +01:00
David Sehnal
af5ddf6950 StructureHierarchy now relies less on tags 2020-03-23 15:03:38 +01:00
David Sehnal
2ef35e5fb9 CustomElementProperty label fix 2020-03-23 13:43:52 +01:00
David Sehnal
513bfeaae7 StructureHierarchyManager sync on demand
+ PluginComponent updates
2020-03-23 11:01:37 +01:00
David Sehnal
7311e6f484 mol-plugin-state: StructureCoordinateSystem decorator (wip)
+ SymmetryOperator.assembly now optional
2020-03-22 22:08:37 +01:00
David Sehnal
90ecedcae8 mol-state: StateTransformer.Definition.isDecorator
- moved from StateTransform.isDecorator
- when using StateBuilder.apply:
  * decorators are always "inserted"
  * if a node has a decorator, the transformer is applied to the decorator instead (recursive)
2020-03-22 15:09:33 +01:00
David Sehnal
352a20ac48 camera manager focus multiple loci/spheres
+ show Unitcell control for multiple structures
+ misc fixes
2020-03-21 13:07:35 +01:00
David Sehnal
b1d8c5f6ea mol-plugin-ui: left panel tweak 2020-03-21 12:16:45 +01:00
Alexander Rose
6497be9e52 0.6.0-dev.7 2020-03-20 19:14:45 -07:00
Alexander Rose
40a8cee8e5 improved scene bounding sphere (reset) handling 2020-03-20 19:13:26 -07:00
Alexander Rose
112cfcac90 0.6.0-dev.6 2020-03-20 14:21:38 -07:00
David Sehnal
8016fcbd54 mol-plugin-state: use 'deposited' as default value for structure if no assembly is present 2020-03-20 21:37:23 +01:00
David Sehnal
6de97f1d03 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-20 21:22:54 +01:00
David Sehnal
204075bbe0 updateStructureComponent bugfix 2020-03-20 21:22:40 +01:00
David Sehnal
eb448bce37 fix volume streaming with custom props enabled 2020-03-20 20:59:23 +01:00
Alexander Rose
3d09b5cb67 use shallow cloning in PD.merge 2020-03-20 12:53:28 -07:00
Alexander Rose
35d06040f7 update debugHelper when visibility changes 2020-03-20 12:22:07 -07:00
Alexander Rose
0868e81944 improved download density action
- combined pdb providers
2020-03-20 12:08:44 -07:00
Alexander Rose
7efbeb7d0f improved download structure action
- updated pdb-dev url
- added archival pdbe
- combined pdb providers
2020-03-20 11:12:52 -07:00
David Sehnal
815f61b550 StructureHierarchyManagerState: auto-select newly added refs 2020-03-20 16:17:47 +01:00
David Sehnal
c3a90ab499 mol-canvas3d: shouldResetCamera check 2020-03-20 13:35:20 +01:00
David Sehnal
321126afa2 show current selection desc in StructureComponentControls header 2020-03-20 11:52:34 +01:00
Alexander Rose
9ad4a9c3c9 recreate vao to fix attempted binding of deleted attributes 2020-03-19 17:29:04 -07:00
Alexander Rose
b12f7c7ce4 add .name property to ShaderCode 2020-03-19 17:26:43 -07:00
Alexander Rose
6d7d3c0794 unitcell label tweaks 2020-03-19 14:24:32 -07:00
Alexander Rose
92b988a8d5 added PD.merge, always use props from structure.root for custom properties 2020-03-19 14:07:48 -07:00
Alexander Rose
18952ee2bd deepClone fix 2020-03-19 14:03:39 -07:00
David Sehnal
bff07888f9 StructureBuilder.tryCreateComponent* refactoring 2020-03-19 20:32:50 +01:00
David Sehnal
8e6b0b220a do not auto-attach interactions in component manager 2020-03-19 20:12:29 +01:00
David Sehnal
74acb0d078 mol-model-state: fix custom props transform bug 2020-03-19 19:14:11 +01:00
Alexander Rose
d4727eea02 removed console.log statement 2020-03-19 10:07:48 -07:00
Alexander Rose
76d14eba0c fix broken units-representation visual state update 2020-03-19 10:01:57 -07:00
David Sehnal
90d05d9260 fix bug in structure repr interaction 2020-03-19 17:43:11 +01:00
David Sehnal
23b24bbb6c custom props are now included by default
+ structure parent helper now takes decorators into account
+ ui & api tweaks
2020-03-19 15:33:37 +01:00
David Sehnal
91bc6f07c5 mol-plugin-state: added TrajectoryHierarchy presets
refactored representation presets
2020-03-19 12:56:47 +01:00
David Sehnal
9b2181667d mol-plugin-state: do not create empty unit cells 2020-03-19 10:59:35 +01:00
Alexander Rose
40347e3e46 0.6.0-dev.5 2020-03-18 21:16:44 -07:00
Alexander Rose
ed277f6e16 Merge remote-tracking branch 'origin/master' into objects 2020-03-18 21:12:52 -07:00
Alexander Rose
8f2085d8b4 0.6.0-dev.4 2020-03-18 20:59:42 -07:00
Alexander Rose
b5a48ad201 package updates 2020-03-18 20:59:01 -07:00
Alexander Rose
14b900791f wip, unitcell ref 2020-03-18 20:49:30 -07:00
Alexander Rose
de80799e68 allow any Loci in CameraManager.focusLoci 2020-03-18 20:44:54 -07:00
Alexander Rose
53eca387fc take renderable visibility into account for scene bounding-sphere 2020-03-18 20:35:14 -07:00
Alexander Rose
fc3005f271 ui tweaks 2020-03-18 16:47:57 -07:00
Alexander Rose
c95fdff00c tweaked StructureSelectionCategorys 2020-03-18 16:04:09 -07:00
Alexander Rose
3cd9042c72 added helpers to Model, better InitVolumeStreaming.isApplicable check 2020-03-18 15:47:58 -07:00
Alexander Rose
4d3914426e improved LociLabelManager to handle multiple loci 2020-03-18 15:46:29 -07:00
David Sehnal
af1fb7041e removed apps/model-server-query 2020-03-18 18:28:19 +01:00
David Sehnal
e2eb1bf223 model-server: config tweak 2020-03-18 13:49:54 +01:00
David Sehnal
5a64a6f1a3 mol-model: fixed a bug in secondary structure export 2020-03-18 13:45:43 +01:00
David Sehnal
88aa9303d7 model-server: fixed data_source bug, allow fetching source data over http(s) 2020-03-18 13:20:08 +01:00
Alexander Rose
5c77eec184 bounding-sphere fixes
- fixed renderable.spec
- simplify extrema for calculateInvariantBoundingSphere when possible
- fixed Sphere3D.expand
2020-03-17 23:19:26 -07:00
Alexander Rose
a931ed7c01 Merge branch 'master' into objects 2020-03-17 21:46:22 -07:00
Alexander Rose
94cd2b618c wip 2020-03-17 20:17:41 -07:00
Alexander Rose
9c97fc258d better box3d calculation in boundary-helper 2020-03-17 16:35:57 -07:00
Alexander Rose
0ac1cfe555 improved bounding sphere- drop hierarchy in favor of extrema points 2020-03-17 16:28:34 -07:00
Alexander Rose
3942f1bc33 added PickRequired type helper 2020-03-17 15:21:06 -07:00
Alexander Rose
a66e38a901 use unit/structure bounding-sphere in visuals geometry 2020-03-17 12:15:39 -07:00
Alexander Rose
f65f4f4aeb seperate grid and boundary calculation 2020-03-17 12:14:36 -07:00
Alexander Rose
f28b13bf87 shader: fixed broken flat-shaded & flip-sided 2020-03-17 09:48:28 -07:00
David Sehnal
8f211a0785 servers readme tweak 2020-03-17 15:50:05 +01:00
David Sehnal
ba1dfb2851 remove preprocess app from webpack config 2020-03-17 15:35:06 +01:00
David Sehnal
964d56752e 0.6.0-dev.3 2020-03-17 15:32:11 +01:00
David Sehnal
cb6a66eba5 0.6.0-dev.2 2020-03-17 15:25:18 +01:00
David Sehnal
94adf7259d model and volume server configuration and docs 2020-03-17 14:46:28 +01:00
David Sehnal
3c831b549a volume-server JSON config 2020-03-17 12:34:32 +01:00
Alexander Rose
7ccf36a0fa moved unitcell representation to mol-repr/shape 2020-03-16 22:38:46 -07:00
Alexander Rose
b0ee640c12 calc bespoke boundingSphere for unitcell 2020-03-16 22:18:09 -07:00
Alexander Rose
cd6d41a035 allow direct setting of geo bounding sphere
- support mesh, lines, text, spheres, points
2020-03-16 21:20:07 -07:00
Alexander Rose
9c8f1f3e11 wip 2020-03-16 17:20:20 -07:00
Alexander Rose
7c50a5a456 HierarchyHelper: check if the split spheres actually result in a smaller radius 2020-03-16 16:12:13 -07:00
David Sehnal
f3b6703fd4 mol-plugin-ui unsubscribe events 2020-03-16 20:05:06 +01:00
Alexander Rose
60f2716b5a fix missing camera reset from scene with only empty objects 2020-03-16 11:42:11 -07:00
David Sehnal
d7007ef99d CSS oveflow: hidden related fixes 2020-03-16 19:22:01 +01:00
David Sehnal
4aca41cdbb Controls Help in volume streaming controls 2020-03-16 15:54:47 +01:00
David Sehnal
7e31fac99a StructureHierarchy.childRoot for structure/model
- to correctly support decorators like custom props
2020-03-16 15:44:25 +01:00
David Sehnal
3bcf1cb6b5 VolumeStreaming manager & UI 2020-03-16 15:14:56 +01:00
David Sehnal
a29cc74304 StructureSourceControls tweak 2020-03-16 12:00:44 +01:00
David Sehnal
95c43d0864 fix camera.radius = 0 (leads to NaNs) 2020-03-16 11:59:32 +01:00
David Sehnal
369b958335 Refactor Bindings and show them in "Simple settings" 2020-03-16 11:51:05 +01:00
David Sehnal
9a17da8f65 Component UI improvements 2020-03-16 10:59:22 +01:00
Alexander Rose
0c4ba20d6e Merge branch 'master' of https://github.com/molstar/molstar 2020-03-15 23:46:55 -07:00
Alexander Rose
22936ff6c9 canvas3d settings improvements
- added clipping params
- show clipping radius
- ranmed render-style to lighting
2020-03-15 23:46:27 -07:00
David Sehnal
3369ad0bdc Change ParameterControls property name 2020-03-15 23:31:40 +01:00
David Sehnal
20853ef60b StructureSource: hide "actions button" when empty 2020-03-15 23:29:10 +01:00
David Sehnal
da0c66bd8e disable "Selected" button when empty 2020-03-15 23:25:08 +01:00
David Sehnal
44664917ff StateTree: always show remove all button 2020-03-15 23:22:36 +01:00
David Sehnal
ce26e002c7 StructureSource UI 2020-03-15 23:20:00 +01:00
David Sehnal
345b9f546c inline actions and updates in StateTree
mol-plugin code cleanup
2020-03-15 19:50:39 +01:00
David Sehnal
f4ed5e301d mol-plugin: code cleanup 2020-03-15 17:37:54 +01:00
David Sehnal
4818f851b3 measurements ui improvements 2020-03-15 16:54:25 +01:00
David Sehnal
6f2c47c0ca spinSpeed param update 2020-03-15 16:00:53 +01:00
David Sehnal
5e6bc0f0df ui tweaks 2020-03-15 15:47:30 +01:00
David Sehnal
083daa0b76 state undo label 2020-03-15 15:40:29 +01:00
David Sehnal
139a10ba8c do not freeze immer objects 2020-03-15 15:29:50 +01:00
David Sehnal
0b512487f5 add PD.Group.pivot, simplified viewport settings 2020-03-15 15:11:55 +01:00
David Sehnal
a85adfdf1f refactored Repr/Color/Size registry & built-ins
updated component UI
2020-03-15 14:32:04 +01:00
David Sehnal
0171b785af optimized Slider control 2020-03-15 01:37:47 +01:00
David Sehnal
4c1c484af9 PurePluginUIComponent fix 2020-03-15 00:25:53 +01:00
David Sehnal
db7585b6b6 CameraManager 2020-03-15 00:16:25 +01:00
David Sehnal
d263f6d929 allow to undo more than 1 step 2020-03-14 23:17:53 +01:00
David Sehnal
034c28e487 selection history updates
updated measurements ui
added focusLoci/Sphere
Loci stats label fixes
2020-03-14 22:44:36 +01:00
David Sehnal
9a88f57ce6 Unify PluginCommands.Highlight and HighlighAll
Select newly created models and structure for create all models
2020-03-14 20:40:40 +01:00
David Sehnal
28415646a2 StructureComponentManager.updateRepresentations tweak 2020-03-14 17:18:04 +01:00
David Sehnal
b03295ef81 undo button tweak 2020-03-14 16:49:13 +01:00
David Sehnal
7d3e849ff3 undo tweak 2020-03-14 15:24:03 +01:00
David Sehnal
926f20a6a4 mol-state: undo support 2020-03-14 15:21:45 +01:00
David Sehnal
c12d577ce6 Add intersect to Component modify options 2020-03-14 14:17:27 +01:00
David Sehnal
aa8d3a9841 tweak non-convalent interactions size factor 2020-03-14 13:41:59 +01:00
David Sehnal
f11e03eb85 Component UI: show modify operations only if available 2020-03-14 13:36:25 +01:00
David Sehnal
ab996b5189 Selection UI improvements 2020-03-14 13:19:03 +01:00
Alexander Rose
4c91e730a2 various params tweaks
- added more descriptions
- cleaned-up structure related params
- added missing quality categorisation
- clearer picking/granularity param
2020-03-13 23:39:35 -07:00
Alexander Rose
5ca5f44c61 tweaked set svg icons 2020-03-13 18:26:01 -07:00
Alexander Rose
f8a89b1c0d add HighlightMany plugin command 2020-03-13 18:12:30 -07:00
Alexander Rose
99dacef747 check structure equality for StructureElement.Loci 2020-03-13 18:10:20 -07:00
Alexander Rose
24fc37105e revert eslint indent rule (conflicts with switch statement) 2020-03-13 17:16:11 -07:00
Alexander Rose
cbd493d834 use sequential list as default for model-index color theme 2020-03-13 16:59:31 -07:00
Alexander Rose
53dbd1aedf ignore type params in eslint indent rule 2020-03-13 16:27:17 -07:00
Alexander Rose
6ab1af54aa added svg icon shapes for set operations 2020-03-13 15:59:47 -07:00
Alexander Rose
615d9758bb Merge branch 'master' of https://github.com/molstar/molstar 2020-03-13 10:17:20 -07:00
David Sehnal
341f9f8c91 repr/theme/size providers now contain unique name 2020-03-13 17:23:12 +01:00
David Sehnal
ba7d4a5215 customize current interaction, wip refactroring repr registries 2020-03-13 16:33:12 +01:00
David Sehnal
428187ad81 refactoring and tweaks 2020-03-13 13:47:20 +01:00
David Sehnal
fe96172fcd wip structure tools 2020-03-13 13:21:02 +01:00
David Sehnal
8b13be698c mol-plugin-state: StructureHierarchy tweaks 2020-03-13 12:23:04 +01:00
David Sehnal
5edc924e4f unify structure representation params creation 2020-03-13 11:55:06 +01:00
David Sehnal
78c50a4339 build config 2020-03-13 09:49:01 +01:00
Alexander Rose
607284585c intersect modifier for ui selections 2020-03-12 17:46:08 -07:00
Alexander Rose
f7af352f03 package updates 2020-03-12 17:45:10 -07:00
David Sehnal
008ec2c88c use tslib to provide helper functions (saves a lot of code) 2020-03-12 22:53:12 +01:00
David Sehnal
c91074ad91 add watch-viewer npm task for faster build times 2020-03-12 22:19:43 +01:00
David Sehnal
51fbb619e5 UI customization via spec & tweaks 2020-03-12 21:14:20 +01:00
David Sehnal
062c4b335b fix typo 2020-03-12 20:39:46 +01:00
David Sehnal
d4107e273d Merge branch 'master' of https://github.com/molstar/molstar 2020-03-12 20:33:14 +01:00
David Sehnal
4dad67fc2e improved Representation params 2020-03-12 20:33:02 +01:00
David Sehnal
64b0713742 represention preset common props 2020-03-12 20:19:24 +01:00
Alexander Rose
22c45e788e improved renderer and postprocessing params 2020-03-12 11:20:06 -07:00
David Sehnal
75de544669 wip source controls 2020-03-12 18:38:29 +01:00
David Sehnal
768ec10809 removed LociHighligh command extend 2020-03-12 17:29:16 +01:00
David Sehnal
4d5d9be399 wip: source structure manager 2020-03-12 17:26:51 +01:00
David Sehnal
7f4afc111e StructureHierarchyManager.currentTrajectories 2020-03-12 15:06:43 +01:00
David Sehnal
0365f883dd mol-plugin: customuze UI layout in viewport params 2020-03-12 14:04:42 +01:00
David Sehnal
6f98549f31 mol-state: doNotUpdateCurrent false as default 2020-03-12 13:23:49 +01:00
David Sehnal
77920818d9 icon tweak 2020-03-12 13:19:49 +01:00
David Sehnal
0682d54ee2 ActionMenu.Item.disabled 2020-03-12 13:17:11 +01:00
David Sehnal
fbcc3b2fa2 Measurement UI, updated "more options" icon in MappedParam 2020-03-12 12:46:26 +01:00
David Sehnal
c3f47b2ecb mol-plugin-state: component representation update 2020-03-12 12:16:34 +01:00
David Sehnal
8b9f59ac5a mol-plugin: component/selection managers/UI 2020-03-12 12:01:31 +01:00
David Sehnal
362dcabe5c PD.Color.isExpanded property 2020-03-12 07:49:33 +01:00
Alexander Rose
6e205661bd tweak 2020-03-11 23:02:52 -07:00
Alexander Rose
c1a8627702 fix BoundaryHelper edge cases & naming tweaks 2020-03-11 23:00:25 -07:00
Alexander Rose
2c5253943c lint and test fixes 2020-03-11 18:22:16 -07:00
Alexander Rose
03668216fa use EPOS-based boundary-helper throughout 2020-03-11 17:56:04 -07:00
Alexander Rose
37ef234803 wip, improved bounding sphere calculation
- Sphere3D.Hierarchy (one level)
- use hierarchy when merging spheres
- split sphere into two when data unevenly spread
2020-03-11 16:58:21 -07:00
David Sehnal
d25aa97fca ui tweaks 2020-03-11 23:29:36 +01:00
David Sehnal
17dc973305 mol-plugin-state: update label/desc for in custom props transform 2020-03-11 22:08:42 +01:00
David Sehnal
eed4e4a134 tweak 2020-03-11 21:03:21 +01:00
David Sehnal
5fd28c6cad mol-plugin-state: do not update interaction params if they didnt change 2020-03-11 21:00:50 +01:00
David Sehnal
0f69ea197d fix custom property transforms, isBusy behavior, other tweaks 2020-03-11 20:46:57 +01:00
David Sehnal
e1a214e1a8 fix component merging 2020-03-11 18:55:51 +01:00
David Sehnal
e7e14750cb Merge branch 'master' of https://github.com/molstar/molstar 2020-03-11 18:49:46 +01:00
David Sehnal
cb83013967 component ui tweak 2020-03-11 18:49:33 +01:00
Alexander Rose
0a84e1bb7b wip, bounding sphere of spheres calculation improvements 2020-03-11 10:22:51 -07:00
David Sehnal
aee7f4988a removed overpaint and repr helpers 2020-03-11 18:18:42 +01:00
David Sehnal
e762c402fa mol-plugin: component manager options 2020-03-11 18:17:06 +01:00
David Sehnal
4375cae70d structure builder tweaks 2020-03-11 17:26:12 +01:00
David Sehnal
90268a9041 Bundle.fromSubStructure 2020-03-11 16:46:52 +01:00
David Sehnal
a3ebc4df45 mol-plugin: modify component manager/ui 2020-03-11 16:42:59 +01:00
David Sehnal
b2743c76e0 wip mol-plugin managers 2020-03-11 12:58:53 +01:00
Alexander Rose
969a70d571 wip, use EPOS for boundary calculation 2020-03-10 18:50:29 -07:00
Alexander Rose
e6a538dbd7 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-10 17:38:18 -07:00
Alexander Rose
9cffce55c9 added EPOS for bounding sphere calculation 2020-03-10 17:37:57 -07:00
David Sehnal
7bc91d7e99 mol-plugin-state: StructureHierarchy fix 2020-03-10 22:50:41 +01:00
David Sehnal
943f5feb59 mol-plugin-state: TrajectoryFormatProvider 2020-03-10 22:12:41 +01:00
David Sehnal
dc72dbbc97 mol-plugin: PluginContext refactoring 2020-03-10 20:16:41 +01:00
David Sehnal
1513a1e2d2 mol-plugin: code cleanup and refactoring 2020-03-10 19:41:56 +01:00
David Sehnal
82989cae57 mol-plugin-ui: Selection/measurement UI updates 2020-03-10 18:27:20 +01:00
David Sehnal
74f8a5053e refactored StructureSelectionManager 2020-03-10 17:43:32 +01:00
David Sehnal
79f7ea209a wip StructureSelectionManager 2020-03-10 17:14:00 +01:00
David Sehnal
ba65e35c51 refactoring 2020-03-10 15:24:27 +01:00
David Sehnal
5bc4e3ce3b css tweaks 2020-03-10 15:13:43 +01:00
David Sehnal
1c7ac60c11 mol-plugin: state.isBusy behavior 2020-03-10 15:01:24 +01:00
David Sehnal
00cd54b69c mol-state: nested transactions 2020-03-10 14:53:05 +01:00
David Sehnal
90e3a3f0c7 ActionMenu tweak 2020-03-10 13:27:27 +01:00
David Sehnal
5eef3fc42a mol-plugin: Structure components UI 2020-03-10 13:10:47 +01:00
David Sehnal
83a8474731 mol-state: accept partial params for transforms; refactored measurements 2020-03-10 12:15:42 +01:00
Alexander Rose
a5e13c5152 wip, measurements controls improvements 2020-03-09 19:03:15 -07:00
Alexander Rose
b19f53d380 added distanceLabel, angleLabel, dihedralLabel 2020-03-09 19:02:30 -07:00
Alexander Rose
abadef1d2e moved FiniteArray to type helpers 2020-03-09 19:01:17 -07:00
Alexander Rose
a3d976c5b8 consistency fix, Vec3.dihedralAngle now returns radians 2020-03-09 19:00:56 -07:00
Alexander Rose
414b20f06d added Sphere3D.fromSphere3Ds and Loci.getBundleBoundingSphere 2020-03-09 19:00:07 -07:00
David Sehnal
d03a442b6c StructureHierarchyManager tweak 2020-03-09 18:46:00 +01:00
David Sehnal
e5acce03e5 mol-plugin-ui: StructureComponentControls 2020-03-09 18:42:27 +01:00
David Sehnal
fe714d4515 wip: StructureHierarchyManager 2020-03-09 17:27:59 +01:00
David Sehnal
7959344bca Current Interaction -> Focus, Fix StructureSelectionFromBundle 2020-03-08 17:12:40 +01:00
David Sehnal
b9582f171d rename 2020-03-08 15:41:03 +01:00
David Sehnal
410123d933 plugin/state: helpers & fixes 2020-03-08 15:34:15 +01:00
David Sehnal
ae484443d8 decorator transforms 2020-03-08 14:40:23 +01:00
David Sehnal
9d97e56f03 mol-state: StateObjectSelector.update 2020-03-08 13:39:18 +01:00
David Sehnal
01269ec1ef mol-state: use produce in "old params" update 2020-03-08 13:28:34 +01:00
David Sehnal
a204264bcc label fix 2020-03-07 15:53:06 +01:00
David Sehnal
eb0a048926 mol-plugin-ui: strongly typed icons & icons preview HTML 2020-03-07 14:43:57 +01:00
Alexander Rose
ba1509b37a wip, measurements ui 2020-03-06 18:02:47 -08:00
Alexander Rose
ad379e7a32 improved Interactions params 2020-03-06 16:12:46 -08:00
Alexander Rose
9d45beea3b ui: add option to cycle through select options on click 2020-03-06 16:12:16 -08:00
Alexander Rose
b2d134aeb4 added ObjectKeys type helper function 2020-03-06 15:07:20 -08:00
Alexander Rose
d500b8ea19 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 13:07:24 -08:00
Alexander Rose
8b3df4b373 plugin-state: preset improvements 2020-03-06 13:07:04 -08:00
David Sehnal
f01fc7c8ba mol-state: log error in transaction 2020-03-06 20:35:57 +01:00
David Sehnal
0e6da0bffa Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 19:10:58 +01:00
David Sehnal
eab8b1c2bf mol-plugin-state: fix in repr presets 2020-03-06 18:46:41 +01:00
Alexander Rose
8b42a0fede Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 08:49:13 -08:00
David Sehnal
4bbe078230 custom property reference counting 2020-03-06 17:03:19 +01:00
David Sehnal
aabb931d27 wip Custom Property references 2020-03-06 16:53:24 +01:00
David Sehnal
b1c1b21975 mol-plugin-state: representation components/presets 2020-03-06 14:57:31 +01:00
David Sehnal
7b5dc3ef96 PD.ValuesFor 2020-03-06 14:12:33 +01:00
David Sehnal
1f831f90e0 mol-plugin-state: StructureBuilder 2020-03-06 13:54:14 +01:00
Alexander Rose
ef2c1b51e9 tweaked geometry param info categories 2020-03-05 16:40:19 -08:00
David Sehnal
ace73c041b fix CustomElementProperty color theme 2020-03-05 10:44:55 +01:00
David Sehnal
3c70fe5303 support hiding remote state 2020-03-05 10:03:51 +01:00
Alexander Rose
137e23c025 wip, interactions options for representation panel 2020-03-04 18:18:56 -08:00
Alexander Rose
969a19d515 added defaultParams to CustomProperty.Provider 2020-03-04 18:18:05 -08:00
Alexander Rose
7536abe96c workaround for buggy gl_FrontFacing
- e.g. on some integrated Intel GPUs
2020-03-04 16:27:11 -08:00
Alexander Rose
8e20e163f9 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-04 15:09:16 -08:00
Alexander Rose
adbe8e1f67 take parent structure into account for surface calculations
- new option `includeParent`
2020-03-04 15:08:46 -08:00
David Sehnal
5db134f34f mol-plugin-state: StructureComponent 2020-03-04 19:20:52 +01:00
David Sehnal
80cf7c1dd2 wip state builders 2020-03-04 15:01:46 +01:00
David Sehnal
9e5cc184ed State transactions, better error message for failed downloads 2020-03-04 13:44:38 +01:00
David Sehnal
a5185b456c mol-plugin-state: DataBuilder 2020-03-04 12:56:17 +01:00
Alexander Rose
0615198ad2 0.5.6 2020-03-03 15:13:50 -08:00
Alexander Rose
6cd422f5b3 package updates 2020-03-03 15:13:16 -08:00
Alexander Rose
4416178d6f fix error handling in readData 2020-03-03 14:52:11 -08:00
David Sehnal
b8cef99dc1 ParameterControls fix 2020-03-03 23:04:56 +01:00
David Sehnal
4dcb68af33 ParamDefinition category and hideIf support 2020-03-03 22:45:20 +01:00
David Sehnal
b4c70ab14a fix camera reset when loading stored state 2020-03-03 17:29:51 +01:00
David Sehnal
8436e17af9 removed PD.categories, added PD.isEssential & UI support 2020-03-03 16:05:56 +01:00
David Sehnal
bda04cbdc7 ParamDefinition categories (wip) 2020-03-03 15:27:10 +01:00
David Sehnal
7287947a55 added mol-plugin-state, refactored PluginCommand 2020-03-03 13:46:34 +01:00
David Sehnal
fb5eb1e19c Merge branch 'master' into plugin 2020-03-03 13:11:51 +01:00
David Sehnal
d735dcdc3e mol-plugin-ui: ActionMenu helpers 2020-03-03 13:09:51 +01:00
Alexander Rose
83d8a61400 0.5.5 2020-03-02 17:22:34 -08:00
Alexander Rose
b72f57c040 assembly symmetry tweaks 2020-03-02 17:21:07 -08:00
Alexander Rose
8a7ef1c704 added StructureOverpaintHelper.setFromExpression and alpha prop 2020-03-02 16:28:10 -08:00
Alexander Rose
8731bc13d1 StructureElement.Loci helpers
- firstElement
- firstResidue
- firstChain
2020-03-02 16:27:37 -08:00
Alexander Rose
d103cda0c8 StructureSelectionQuery improvements
- added categories
- StructureSelectionQueryList with standard residues
2020-03-02 14:04:27 -08:00
Alexander Rose
ca4e864e46 StructureRepresentationHelper.getRepresentationParams tweaks 2020-03-02 13:02:27 -08:00
Alexander Rose
0e25950d51 don't remove C! assembly symmetry 2020-03-02 13:01:35 -08:00
David Sehnal
2c29a90b5e fix 2020-03-02 15:32:24 +01:00
David Sehnal
2900836208 mol-canvas3d: requestCameraReset options 2020-03-02 15:28:20 +01:00
Alexander Rose
928a1df525 handle nullish values in SelectControl 2020-02-29 22:09:13 -08:00
Alexander Rose
16e0dff551 molecule types improvements 2020-02-29 22:08:29 -08:00
David Sehnal
7e443d5c9b mol-plugin-ui: simplified ActionMenu, fixed residue/amk selection 2020-02-29 18:15:39 +01:00
David Sehnal
172ae17966 mol-plugin-ui: SelectControl label fix 2020-02-29 15:40:28 +01:00
David Sehnal
8cbf7b5d0a mol-plugin-ui: Controls improvements 2020-02-29 15:27:21 +01:00
Alexander Rose
2c40b85880 0.5.4 2020-02-28 18:29:15 -08:00
Alexander Rose
ad388f23ae package updates 2020-02-28 18:28:39 -08:00
Alexander Rose
27b559a2d8 tweaked color-theme categories 2020-02-28 18:08:33 -08:00
Alexander Rose
c6d19e14c5 improved naming of PDBe and RCSB PDB provided properties 2020-02-28 17:46:10 -08:00
Alexander Rose
b807dca2d8 wip, StructureRepresentationHelper tweaks 2020-02-28 17:13:00 -08:00
Alexander Rose
2cae6e3f59 pass correct themeDataCtx in StructureRepresentation3DHelpers.createParams 2020-02-28 17:12:09 -08:00
Alexander Rose
69c73f3dcd StructureSelectionQuery improvements
- delayed query compilation to work with CustomPropSymbol
- optional async ensureCustomProperties method
- added hasClash, isBuried, isAccessible built-ins
- integrate .ensureCustomProperties with StructureSelectionHelper
2020-02-28 17:11:06 -08:00
Alexander Rose
4456ab2cd5 whitespace 2020-02-28 17:06:13 -08:00
Alexander Rose
bdda18de23 added mol-script symbol to ValidationReport 2020-02-28 17:05:22 -08:00
Alexander Rose
1b2c2f3d41 added Tag enum with common strings to AssemblySymmetry 2020-02-28 17:04:55 -08:00
Alexander Rose
fd19d29ef6 accessible surface area improvements
- added mol-script symbols
- helpers to get normalized value and flag
2020-02-28 17:04:03 -08:00
David Sehnal
1f0c0fd756 mol-geo: VisualQuality label fix 2020-02-27 16:56:28 +01:00
David Sehnal
83698cc52d mol-plugin-ui: ActionMenu fixes 2020-02-27 16:53:11 +01:00
David Sehnal
70b94deb20 tweak 2020-02-27 16:19:46 +01:00
David Sehnal
2da3df6e4d ParamDefinition.Select grouping & used in theme definitions 2020-02-27 16:17:23 +01:00
David Sehnal
509e633a69 mol-plugin-ui: use ActionMenu in SelectControl 2020-02-27 15:09:47 +01:00
David Sehnal
17fac2b82a mol-plugin-ui: ActionMenu refactoring 2020-02-27 14:36:48 +01:00
Alexander Rose
c543c4e10a 0.5.3 2020-02-26 17:20:24 -08:00
Alexander Rose
10073800dc added amino acids and nucleic bases queries 2020-02-26 17:14:40 -08:00
Alexander Rose
04c38250b4 tweaked action-menu offset 2020-02-26 17:14:08 -08:00
Alexander Rose
5ccb329af1 added SetUtils.toArray 2020-02-26 17:13:34 -08:00
Alexander Rose
3cecb53bc5 added model.properties.structAsymMap 2020-02-26 15:04:48 -08:00
Alexander Rose
0daf431d68 added and use cross-link loci/location 2020-02-26 14:23:51 -08:00
Alexander Rose
3e0c4242ad simplified interactions loci/location 2020-02-26 14:23:03 -08:00
Alexander Rose
17b775c377 areDataLociEqual, use shallowEqual to compare data objects 2020-02-26 14:22:05 -08:00
Alexander Rose
4525b98288 SecondaryStructure improvements
- use SecondaryStructureProvider in StructureProperties
- renamed mmCIF source to model
2020-02-26 11:43:15 -08:00
Alexander Rose
755699d479 fix structure/model props attachment 2020-02-26 11:37:57 -08:00
Alexander Rose
a84309b800 add mon_nstd_flag when deriving chemComp 2020-02-26 10:37:36 -08:00
Alexander Rose
16863535b8 cleanup, removed comment 2020-02-26 10:36:37 -08:00
Alexander Rose
dd9773d72e fix pdb parser: need cif-category not table 2020-02-26 10:36:12 -08:00
Alexander Rose
ec45f6c0ee removed 'modified residues'
- use non-standard flag in polymers residues instead to show interesting residues
- 'modified residues' is of limited value to get parent, better rely on chem_comp.type
2020-02-26 09:51:54 -08:00
David Sehnal
59235630bb mol-plugin-ui: ActionMenu, use in selection manager to test 2020-02-26 18:43:56 +01:00
David Sehnal
11ed0ca89b added draft of ParamMapping interface and control 2020-02-26 16:39:24 +01:00
Alexander Rose
188ea6e8e2 skin: fix select button hidden issue in FF 2020-02-25 22:06:52 -08:00
Alexander Rose
293b464d9f moved cross-link-restraint to props 2020-02-25 17:40:08 -08:00
Alexander Rose
0e91aa521e added IntervalControl, use 'step' for precision, allow 'range' in Vec3 2020-02-25 17:39:34 -08:00
Alexander Rose
5272593cc3 ermoved deprecated StructureAssemblyFromModel 2020-02-25 15:10:12 -08:00
Alexander Rose
c5f336b0e4 fix Assembly Symmetry Cluster coloring for deposited model 2020-02-25 14:53:37 -08:00
Alexander Rose
b1a6fa3ffc package updates, remove tslint related package 2020-02-25 14:52:55 -08:00
Alexander Rose
fe47134934 0.5.2 2020-02-25 12:00:59 -08:00
Alexander Rose
131cc606f0 added support to build custom assembly from symmetry operations and asym ids 2020-02-25 11:57:58 -08:00
Alexander Rose
3681f01fad added Spacegroup.getOperatorXyz 2020-02-25 11:56:48 -08:00
Alexander Rose
e966c112ab assymbly-symmetry: filter C1, export AssemblySymmetry3D 2020-02-24 17:25:49 -08:00
David Sehnal
d7b232b00b Merge branch 'master' into plugin 2020-02-24 10:12:35 +01:00
Alexander Rose
7986509ad3 0.5.1 2020-02-21 16:54:42 -08:00
Alexander Rose
73dcf970f3 fix fog handling so fog can be fully switched off 2020-02-21 16:26:14 -08:00
Alexander Rose
9377aa2d05 renamed renderstyle 'toon' to 'flat'
- reflects what the options is actually doing
2020-02-21 16:05:35 -08:00
Alexander Rose
686fa5a5ed package updates 2020-02-21 16:05:01 -08:00
Alexander Rose
c946ae6eab updated schemas, emmit .ts instead of .d.ts 2020-02-21 16:01:41 -08:00
David Sehnal
9b2f1d9415 updated to TypeScript 3.8 2020-02-21 17:13:10 +01:00
Alexander Rose
f6c28aa8e2 0.5.0 2020-02-20 16:25:38 -08:00
David Sehnal
2c327cfdf6 vscode tasks 2020-02-12 12:06:52 +01:00
David Sehnal
307f2efc97 Merge branch 'master' into plugin 2020-02-12 12:02:51 +01:00
David Sehnal
64c51f0d94 Merge branch 'master' into plugin 2020-02-10 11:51:29 +01:00
David Sehnal
7a0b4c4d23 Viewer: load from URL 2019-12-24 11:37:43 +01:00
David Sehnal
f45edbc4f0 mol-theme: truncate too long entry names 2019-12-24 11:27:47 +01:00
David Sehnal
65d3355b18 mol-plugin: DataManager [wip] 2019-12-23 17:19:38 +01:00
1148 changed files with 53499 additions and 49987 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,9 +34,9 @@
],
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/quotes": [
"warn",
"error",
"single",
{
{
"avoidEscape": true,
"allowTemplateLiterals": 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

1
.npmignore Normal file
View File

@@ -0,0 +1 @@
tsconfig.servers.buildinfo

14
.vscode/tasks.json vendored
View File

@@ -9,6 +9,20 @@
"problemMatcher": [
"$tsc"
]
},
{
"type": "npm",
"script": "build-tsc",
"problemMatcher": [
"$tsc"
]
},
{
"type": "npm",
"script": "watch",
"problemMatcher": [
"$tsc"
]
}
]
}

View File

@@ -5,7 +5,7 @@
# Mol*
The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will serve as basis for the next-generation data delivery and analysis tools for macromolecular structure data. This is a collaboration between PDBe and RCSB PDB teams and the development will be open source and available to anyone who wants to use it for developing visualisation tools for macromolecular structure data available from [PDB](https://www.wwpdb.org/) and other institutions.
The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will serve as a basis for the next-generation data delivery and analysis tools for macromolecular structure data. This is a collaboration between PDBe and RCSB PDB teams and the development will be open-source and available to anyone who wants to use it for developing visualization tools for macromolecular structure data available from [PDB](https://www.wwpdb.org/) and other institutions.
This particular project is the implementation of this technology (still under development).
@@ -16,25 +16,26 @@ This particular project is the implementation of this technology (still under de
The core of Mol* currently consists of these modules (see under `src/`):
- `mol-task` Computation abstraction with progress tracking and cancellation support.
- `mol-data` Collections (integer based sets, interface to columns/tables, etc.)
- `mol-data` Collections (integer-based sets, interface to columns/tables, etc.)
- `mol-math` Math related (loosely) algorithms and data structures.
- `mol-io` Parsing library. Each format is parsed into an interface that corresponds to the data stored by it. Support for common coordinate, experimental/map, and annotation data formats.
- `mol-model` Data structures and algorithms (such as querying) for representing molecular data (including coordinate, experimental/map, and annotation data).
- `mol-model-formats` Data format parsers for `mol-model`.
- `mol-model-props` Common "custom properties".
- `mol-script` A scriting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)).
- `mol-script` A scripting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)).
- `mol-geo` Creating (molecular) geometries.
- `mol-theme` Theming for structure, volume and shape representations.
- `mol-repr` Molecular representations for structures, volumes and shapes.
- `mol-gl` A wrapper around WebGL.
- `mol-canvas3d` A low level 3d view component. Uses `mol-geo` to generate geometries.
- `mol-canvas3d` A low-level 3d view component. Uses `mol-geo` to generate geometries.
- `mol-state` State representation tree with state saving and automatic updates.
- `mol-app` Components for builduing UIs.
- `mol-app` Components for building UIs.
- `mol-plugin` Allow to define modular Mol* plugin instances utilizing `mol-state` and `mol-canvas3d`.
- `mol-plugin-ui` React based user interface for the Mol* plugin. Some components of the UI are usable outside the main plugin and can be integrated to 3rd party solutions.
- `mol-plugin-state` State transformations, builders, and managers.
- `mol-plugin-ui` React-based user interface for the Mol* plugin. Some components of the UI are usable outside the main plugin and can be integrated into 3rd party solutions.
- `mol-util` Useful things that do not fit elsewhere.
Moreover, the project contains the imlementation of `servers`, including
Moreover, the project contains the implementation of `servers`, including
- `servers/model` A tool for accessing coordinate and annotation data of molecular structures.
- `servers/volume` A tool for accessing volumetric experimental data related to molecular structures.
@@ -61,6 +62,8 @@ This project builds on experience from previous solutions:
### Build automatically on file save:
npm run watch
If working on just the viewer, ``npm run watch-viewer`` will provide shorter compile times.
### Build with debug mode enabled:
DEBUG=molstar npm run watch
@@ -86,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**
@@ -112,22 +114,26 @@ Install CIFTools `npm install ciftools -g`
**Convert any CIF to BinaryCIF**
node build/model-server/preprocess -i file.cif -ob file.bcif
node lib/servers/model/preprocess -i file.cif -ob file.bcif
To see all available commands, use ``node build/model-server/preprocess -h``.
To see all available commands, use ``node lib/servers/model/preprocess -h``.
Or
node ./lib/apps/cif2bcif
## Development
### Intallation
### Installation
If node complains about a missine acorn peer dependency, run the following commands
If node complains about a missing acorn peer dependency, run the following commands
npm update acorn --depth 20
npm dedupe
### Editor
To get syntax highlighting for shader and graphql files add the following to Visual Code's settings files and make sure relevant extanesions are installed in the editor.
To get syntax highlighting for shader and graphql files add the following to Visual Code's settings files and make sure relevant extensions are installed in the editor.
"files.associations": {
"*.glsl.ts": "glsl",
@@ -139,7 +145,7 @@ To get syntax highlighting for shader and graphql files add the following to Vis
## Publish
### Prerelease
npm version prerelease # asumes the current version ends with '-dev.X'
npm version prerelease # assumes the current version ends with '-dev.X'
npm publish --tag next
### Release
@@ -161,4 +167,4 @@ Continually develop this prototype project. As individual modules become stable,
Funding sources include but are not limited to:
* [RCSB PDB](https://www.rcsb.org) funding by a grant [DBI-1338415; PI: SK Burley] from the NSF, the NIH, and the US DoE
* [PDBe, EMBL-EBI](https://pdbe.org)
* [CEITEC](https://www.ceitec.eu/)
* [CEITEC](https://www.ceitec.eu/)

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

@@ -6,133 +6,64 @@ Model Server is a tool for preprocessing and querying macromolecular structure d
Installing and Running
=====================
Getting the code (use node 8+):
Requires nodejs 8+.
## From GitHub
```
git clone https://github.com/molstar/molstar
npm install
```
Customize configuration at ``src/server/model/config.ts`` to point to your data and which custom properties to include (see the [Custom Properties](#custom-properties) section). Alternatively, the config can be edited in the compiled version in ``build/node_modules/servers/model/config.js``.
Afterwards, build the project:
Afterwards, build the project source:
```
npm run build
npm run build-tsc
```
(or run watch mode for automatic rebuilds: ``npm run watch``)
and run the server by
Running the server locally for testing:
```
npm run model-server
```
or
```
node build/node_modules/servers/model/server
node lib/servers/model/server/server
```
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
## From NPM
```
npm install --production molstar
./model-server
```
(or ``node node_modules\.bin\model-server`` in Windows).
The NPM package contains all the tools mentioned here as "binaries":
- ``model-server``
- ``model-server-query``
- ``model-server-preprocess``
## Memory issues
### Production use
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Memory issues
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
Preprocessor
============
## Preprocessor
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. See the [Custom Properties](#custom-properties) section for providing custom properties.
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/servers/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
## Usage
The app works in two modes: single files and folders.
## Local Mode
Single files:
```
node build\node_modules\servers\model\preprocess -i input.cif [-oc output.cif] [-ob output.bcif] [--cfg config.json]
```
Folder:
```
node build\node_modules\servers\model\preprocess -fin input_folder [-foc output_cif_folder] [-fob output_bcif_folder] [--cfg config.json]
```
## Config
The config speficies the maximum number of processes to use (in case of folder processing) and defines sources and parameters for custom properties.
Example:
```json
{
"numProcesses": 4,
"customProperties": {
"sources": [
"./properties/pdbe"
],
"params": {
"PDBe": {
"UseFileSource": false,
"API": {
"residuewise_outlier_summary": "https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry",
"preferred_assembly": "https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary",
"struct_ref_domain": "https://www.ebi.ac.uk/pdbe/api/mappings/sequence_domains"
}
}
}
}
}
```
The server can be run in local/file based mode using ``node lib/servers/servers/model/query`` (``model-server-query`` binary from the NPM package).
Custom Properties
=================
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.
This feature is still in development.
Local Mode
==========
The server can be run in local/file based mode:
```
node build/node_modules/servers/model/server jobs.json
```
where ``jobs.json`` is an array of
```ts
type LocalInput = {
input: string,
output: string,
query: QueryName,
modelNums?: number[],
params?: any,
binary?: boolean
}[]
```
For example
```json
[
{
"input": "c:/test/quick/1tqn.cif",
"output": "c:/test/quick/localapi/1tqn_full.cif",
"query": "full"
},
{
"input": "c:/test/quick/1tqn.cif",
"output": "c:/test/quick/localapi/1tqn_full.bcif",
"query": "full",
"params": {}
},
{
"input": "c:/test/quick/1cbs_updated.cif",
"output": "c:/test/quick/localapi/1cbs_ligint.cif",
"query": "residueInteraction",
"params": {
"atom_site": { "label_comp_id": "REA" }
}
}
]
```
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.

View File

@@ -7,107 +7,64 @@ It uses the text based CIF and BinaryCIF formats to deliver the data to the clie
For quick info about the benefits of using the server, check out the [examples](examples.md).
Installing the Server
Installing and Running
=====================
- Install [Node.js](https://nodejs.org/en/) (tested on Node 6.* and 7.*; x64 version is strongly preferred).
- Get the code.
- Prepare the data.
- Run the server.
Requires nodejs 8+.
Preparing the Data
------------------
## From GitHub
```
git clone https://github.com/molstar/molstar
npm install
```
Afterwards, build the project source:
```
npm run build-tsc
```
and run the server by
```
node lib/servers/servers/volume/server
```
## From NPM
```
npm install --production molstar
./volume-server
```
(or ``node node_modules\.bin\volume-server`` in Windows).
The NPM package contains all the tools mentioned here as "binaries":
- ``volume-server``
- ``volume-server-pack``
- ``volume-server-query``
### Production use
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Memory issues
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
## Preparing the Data
For the server to work, CCP4/MAP (models 0, 1, 2 are supported) input data need to be converted into a custom block format.
To achieve this, use the ``pack`` application.
To achieve this, use the ``pack`` application (``node lib/servers/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
- To prepare data from x-ray based methods, use:
## Local Mode
```
node pack -xray main.ccp4 diff.ccp4 out.mdb
```
- For EM data, use:
```
node pack -em em.map out.mdb
```
Running the Server
------------------
- Install production dependencies:
```
npm install --only=production
```
- Update ``server-config.js`` to link to your data and optionally tweak the other parameters.
- Run it:
```
node server
```
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Local Mode
The program ``local`` in the build folder can be used to query the data without running a http server.
- ``node local`` prints the program usage.
- ``node local jobs.json`` takes a list of jobs to execute in JSON format. A job entry is defined by this interface:
```TypeScript
interface JobEntry {
source: {
filename: string,
name: string,
id: string
},
query: {
kind: 'box' | 'cell',
space?: 'fractional' | 'cartesian',
bottomLeft?: number[],
topRight?: number[],
}
params: {
/** Determines the detail level as specified in server-config */
detail?: number,
/**
* Determines the sampling level:
* 1: Original data
* 2: Downsampled by factor 1/2
* ...
* N: downsampled 1/2^(N-1)
*/
forcedSamplingLevel?: number,
asBinary: boolean,
},
outputFolder: string
}
```
Example ``jobs.json`` file content:
```TypeScript
[{
source: {
filename: `g:/test/mdb/emd-8116.mdb`,
name: 'em',
id: '8116',
},
query: {
kind: 'cell'
},
params: {
detail: 4,
asBinary: true
},
outputFolder: 'g:/test/local-test'
}]
```
The program ``lib/servers/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
## Navigating the Source Code
@@ -122,8 +79,8 @@ The source code is split into 2 mains parts: ``pack`` and ``server``:
Consuming the Data
==================
The data can be consumed in any (modern) browser using the [CIFTools.js library](https://github.com/dsehnal/CIFTools.js) (or any other piece of code that can read text or binary CIF).
The data can be consumed in any (modern) browser using the [ciftools library](https://github.com/molstar/ciftools) (or any other piece of code that can read text or binary CIF).
The [Data Format](DataFormat.md) document gives a detailed description of the server response format.
As a reference/example of the server usage, please see the implementation in [LiteMol](https://github.com/dsehnal/LiteMol) ([CIF.ts + Data.ts](https://github.com/dsehnal/LiteMol/tree/master/src/lib/Core/Formats/Density), [UI](https://github.com/dsehnal/LiteMol/tree/master/src/Viewer/Extensions/DensityStreaming)) or in Mol*.
As a reference/example of the server usage is available in Mol* ``mol-plugin`` module.

10752
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.5.0-dev.3",
"version": "0.7.0-dev.11",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -11,21 +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",
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
"build-webpack": "webpack --mode production",
"watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"watch-tsc": "tsc -watch",
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
"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-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": "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"
@@ -33,6 +41,16 @@
"files": [
"lib/"
],
"bin": {
"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": [
"./node_modules",
@@ -64,65 +82,68 @@
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^1.12.2",
"@graphql-codegen/cli": "^1.12.2",
"@graphql-codegen/time": "^1.12.2",
"@graphql-codegen/typescript": "^1.12.2",
"@graphql-codegen/typescript-graphql-files-modules": "^1.12.2",
"@graphql-codegen/typescript-graphql-request": "^1.12.2",
"@graphql-codegen/typescript-operations": "^1.12.2",
"@graphql-codegen/add": "^1.13.3",
"@graphql-codegen/cli": "^1.13.3",
"@graphql-codegen/time": "^1.13.3",
"@graphql-codegen/typescript": "^1.13.3",
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.3",
"@graphql-codegen/typescript-graphql-request": "^1.13.3",
"@graphql-codegen/typescript-operations": "^1.13.3",
"@types/cors": "^2.8.6",
"@typescript-eslint/eslint-plugin": "^2.19.2",
"@typescript-eslint/eslint-plugin-tslint": "^2.19.2",
"@typescript-eslint/parser": "^2.19.2",
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.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.3",
"eslint": "^6.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^5.0.2",
"fs-extra": "^8.1.0",
"file-loader": "^6.0.0",
"fs-extra": "^9.0.0",
"graphql": "^15.0.0",
"http-server": "^0.12.1",
"jest": "^25.1.0",
"jest": "^25.4.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",
"node-sass": "^4.14.0",
"raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"simple-git": "^1.131.0",
"style-loader": "^1.1.3",
"ts-jest": "^25.2.0",
"typescript": "^3.7.5",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
"simple-git": "^1.132.0",
"style-loader": "^1.2.0",
"ts-jest": "^25.4.0",
"typescript": "^3.8.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-version-file-plugin": "^0.4.0"
},
"dependencies": {
"@material-ui/core": "^4.9.11",
"@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.2",
"@types/jest": "^25.1.2",
"@types/node": "^13.7.0",
"@types/node-fetch": "^2.5.4",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/express": "^4.17.6",
"@types/jest": "^25.2.1",
"@types/node": "^13.13.2",
"@types/node-fetch": "^2.5.7",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"@types/swagger-ui-dist": "3.0.5",
"argparse": "^1.0.10",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"graphql": "^14.6.0",
"immer": "^6.0.3",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"rxjs": "^6.5.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"rxjs": "^6.5.5",
"swagger-ui-dist": "^3.25.0",
"tslib": "^1.11.1",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.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,94 +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 { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateBuilder } from '../../mol-state';
import Expression from '../../mol-script/language/expression';
import { BuiltInColorThemeName } from '../../mol-theme/color';
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,
{ 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.TransformStructureConformationByMatrix, { matrix }, { tags: 'transform' });
}
export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
return b.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: id || 'deposited' }, { tags: 'asm' })
}
export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'), { tags: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'), { tags: 'het-visual' });
// visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
// .apply(StateTransforms.Representation.StructureRepresentation3D,
// StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }), { tags: 'water-visual' });
return visualRoot;
}
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, coloring?: BuiltInColorThemeName) {
visualRoot
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, coloring), { tags: 'het-visual' });
return visualRoot;
}
}

View File

@@ -1,206 +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/command';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
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';
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.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
this.plugin.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);
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.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 }), { ref: 'water-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill'), { ref: 'ihm-visual' });
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.dataState;
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.dispatch(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
}
setBackground(color: number) {
const renderer = this.plugin.canvas3d!.props.renderer;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
}
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.dataState;
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
const tree = state.build();
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
for (const v of visuals) {
tree.to(v).update(old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
}
}
interactivity = {
highlightOn: () => {
const seq_id = 7;
const data = (this.plugin.state.dataState.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.interactivity.lociHighlights.highlightOnly({ loci });
},
clearHighlight: () => {
this.plugin.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
tests = {
staticSuperposition: async () => {
const state = this.plugin.state.dataState;
const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: StateTransform.RootRef });
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
},
dynamicSuperposition: async () => {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
},
toggleValidationTooltip: async () => {
const state = this.plugin.state.behaviorState;
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
},
showToasts: () => {
PluginCommands.Toast.Show.dispatch(this.plugin, {
title: 'Toast 1',
message: 'This is an example text, timeout 3s',
key: 'toast-1',
timeoutMs: 3000
});
PluginCommands.Toast.Show.dispatch(this.plugin, {
title: 'Toast 2',
message: CustomToastMessage,
key: 'toast-2'
});
},
hideToasts: () => {
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide.dispatch(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/command';
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.dataState.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.dataState;
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.dispatch(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.dispatch(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: string[] = []
const pdbx_aromatic_flag: string[] = []
const pdbx_stereo_config: string[] = []
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,166 +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/command';
import { StateTransforms } from '../../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
import { StateBuilder } from '../../../mol-state';
import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
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.dispatch(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);
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill', {}, 'illustrative'), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
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.dataState;
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.dispatch(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
}
}
(window as any).LightingDemo = new LightingDemo();

View File

@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Mol* ModelServer Query Builder</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

View File

@@ -1,134 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import * as Rx from 'rxjs'
import { QueryDefinition, QueryList } from '../../servers/model/server/api'
import './index.html'
interface State {
query: Rx.BehaviorSubject<QueryDefinition>,
id: Rx.BehaviorSubject<string>,
params: Rx.BehaviorSubject<any>,
isBinary: Rx.BehaviorSubject<boolean>,
models: Rx.BehaviorSubject<number[]>,
url: Rx.Subject<string>
}
class Root extends React.Component<{ state: State }, { }> {
render() {
return <div>
<div>
Query: <QuerySelect state={this.props.state} />
</div>
<div>
ID: <input type='text' onChange={t => this.props.state.id.next(t.currentTarget.value)} />
</div>
<div>
Params:<br/>
<QueryParams state={this.props.state} />
</div>
<div>
Model numbers (empty for all): <ModelNums state={this.props.state} />
</div>
<div>
<input type='checkbox' onChange={t => this.props.state.isBinary.next(!!t.currentTarget.checked)} /> Binary
</div>
<div>
Query string:
<QueryUrl state={this.props.state} />
</div>
</div>
}
}
class QuerySelect extends React.Component<{ state: State }> {
render() {
return <select onChange={s => this.props.state.query.next(QueryList[+s.currentTarget.value].definition)}>
{ QueryList.map((q, i) => <option value={i} key={i} selected={i === 1}>{q.definition.niceName}</option>) }
</select>
}
}
class QueryParams extends React.Component<{ state: State }, { prms: string }> {
state = { prms: '' };
parseParams(str: string) {
this.setState({ prms: str });
try {
const params = JSON.parse(str);
this.props.state.params.next(params);
} catch {
this.props.state.params.next({});
}
}
componentDidMount() {
this.props.state.query.subscribe(q => this.setState({ prms: formatParams(q) }))
}
render() {
return <textarea style={{height: '300px'}} value={this.state.prms} cols={80} onChange={t => this.parseParams(t.currentTarget.value)} />;
}
}
class QueryUrl extends React.Component<{ state: State }, { queryString: string }> {
state = { queryString: '' };
componentDidMount() {
this.props.state.url.subscribe(url => this.setState({ queryString: url }))
}
render() {
return <input type='text' value={this.state.queryString} style={{ width: '800px' }} />
}
}
class ModelNums extends React.Component<{ state: State }> {
render() {
return <input type='text' defaultValue='1' style={{ width: '300px' }} onChange={t =>
this.props.state.models.next(t.currentTarget.value.split(',')
.map(v => v.trim())
.filter(v => !!v)
.map(v => +v)
)} />
}
}
const state: State = {
query: new Rx.BehaviorSubject(QueryList[1].definition),
id: new Rx.BehaviorSubject('1cbs'),
params: new Rx.BehaviorSubject({ }),
isBinary: new Rx.BehaviorSubject<boolean>(false),
models: new Rx.BehaviorSubject<number[]>([]),
url: new Rx.Subject()
}
function formatParams(def: QueryDefinition) {
const prms = Object.create(null);
for (const p of def.jsonParams) {
prms[p.name] = p.exampleValues ? p.exampleValues[0] : void 0;
}
return JSON.stringify(prms, void 0, 2);
}
function formatUrl() {
const json = JSON.stringify({
name: state.query.value.name,
id: state.id.value,
modelNums: state.models.value.length ? state.models.value : void 0,
binary: state.isBinary.value,
params: state.params.value
});
state.url.next(encodeURIComponent(json));
}
Rx.merge(state.query, state.id, state.params, state.isBinary, state.models).subscribe(s => formatUrl());
ReactDOM.render(<Root state={state} />, document.getElementById('app'));

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

@@ -19,8 +19,10 @@ function paramInfo(param: PD.Any, offset: number): string {
case 'conditioned': return getParams(param.conditionParams, offset);
case 'multi-select': return `Array of ${oToS(param.options)}`;
case 'color': return 'Color as 0xrrggbb';
case 'color-list': return `One of ${oToS(param.options)}`;
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)}`;
@@ -39,7 +41,7 @@ function paramInfo(param: PD.Any, offset: number): string {
}
}
function oToS(options: readonly (readonly [string, string])[]) {
function oToS(options: readonly (readonly [string, string] | readonly [string, string, string | undefined])[]) {
return options.map(o => `'${o[0]}'`).join(', ');
}

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,25 +116,13 @@ 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)}`);
}
console.log();
}
export function printModRes(model: Model) {
console.log('\nModified Residues\n=============');
const map = model.properties.modifiedResidues.parentId;
const { label_comp_id, _rowCount } = model.atomicHierarchy.residues;
for (let i = 0; i < _rowCount; i++) {
const comp_id = label_comp_id.value(i);
if (!map.has(comp_id)) continue;
console.log(`[${i}] ${map.get(comp_id)} -> ${comp_id}`);
}
console.log();
}
export function printRings(structure: Structure) {
console.log('\nRings\n=============');
for (const unit of structure.units) {
@@ -144,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();
@@ -183,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(', ')}`);
@@ -221,12 +209,11 @@ async function run(frame: CifFrame, args: Args) {
if (args.rings) printRings(structure);
if (args.intraBonds) printBonds(structure, true, false);
if (args.interBonds) printBonds(structure, false, true);
if (args.mod) printModRes(models[0]);
if (args.sec) printSecStructure(models[0]);
}
async function runDL(pdb: string, args: Args) {
const mmcif = await downloadFromPdb(pdb)
const mmcif = await downloadFromPdb(pdb);
run(mmcif, args);
}
@@ -268,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,480 +0,0 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateAction } from '../../../../mol-state';
import { PluginContext } from '../../../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Ingredient, CellPacking, Cell } from './data';
import { getFromPdb, getFromCellPackDB } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../../../mol-math/geometry';
import { Task } from '../../../../mol-task';
import { StructureRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation';
import { StateTransforms } from '../../../../mol-plugin/state/transforms';
import { distinctColors } from '../../../../mol-util/color/distinct';
import { ModelIndexColorThemeProvider } from '../../../../mol-theme/color/model-index';
import { Hcl } from '../../../../mol-util/color/spaces/hcl';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../../../mol-script/runtime/query/compiler';
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
import { ColorTheme } from '../../../../mol-theme/color';
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';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`
}
async function getModel(id: string, baseUrl: string) {
let model: Model;
if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
// return
const cif = await getFromPdb(id)
model = (await trajectoryFromMmCIF(cif).run())[0]
} else {
const pdb = await getFromCellPackDB(id, baseUrl)
model = (await trajectoryFromPDB(pdb).run())[0]
}
return model
}
async function getStructure(model: Model, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model)
const { assembly } = props
if (assembly) {
structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
}
const query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
})
])
const compiled = compile<StructureSelection>(query)
const result = compiled(new QueryContext(structure))
structure = StructureSelection.unionStructure(result)
return structure
}
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
Mat4.transpose(m, m)
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0))
Mat4.setTranslation(m, trans)
return m
}
function getResultTransforms(results: Ingredient['results']) {
return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
}
function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0
const instances: Mat4[] = []
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`
if (!(cname in ingredient)) {
// console.warn(`Expected '${cname}' in ingredient`)
continue
}
const _points = ingredient[cname] as Vec3[]
if (_points.length <= 2) {
// TODO handle curve with 2 or less points
continue
}
const points = new Float32Array(_points.length * 3)
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
const newInstances = getMatFromResamplePoints(points)
instances.push(...newInstances)
}
return instances
}
function getAssembly(transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder()
const { units } = structure;
for (let i = 0, il = transforms.length; i < il; ++i) {
const id = `${i + 1}`
const op = SymmetryOperator.create(id, transforms[i], { id, operList: [ id ] })
for (const unit of units) {
builder.addWithOperator(unit, op)
}
}
return builder.getStructure();
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
const { db } = model.sourceData.data
const d = db.atom_site
const n = d._rowCount
const rowCount = n * transforms.length
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
const x = d.Cartn_x.toArray()
const y = d.Cartn_y.toArray()
const z = d.Cartn_z.toArray()
const Cartn_x = new Float32Array(rowCount)
const Cartn_y = new Float32Array(rowCount)
const Cartn_z = new Float32Array(rowCount)
const map = new Uint32Array(rowCount)
const seq = new Int32Array(rowCount)
let offset = 0
for (let c = 0; c < count; ++c) {
const cStart = offsets[c]
const cEnd = offsets[c + 1]
const cLength = cEnd - cStart
for (let t = 0, tl = transforms.length; t < tl; ++t) {
const m = transforms[t]
for (let j = cStart; j < cEnd; ++j) {
const i = offset + j - cStart
const xj = x[j], yj = y[j], zj = z[j]
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
map[i] = j
seq[i] = t + 1
}
offset += cLength
}
}
function multColumn<T>(column: Column<T>) {
const array = column.toArray()
return Column.ofLambda({
value: row => array[map[row]],
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
rowCount, schema: column.schema
})
}
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
auth_seq_id: CifField.ofNumbers(seq),
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
Cartn_x: CifField.ofNumbers(Cartn_x),
Cartn_y: CifField.ofNumbers(Cartn_y),
Cartn_z: CifField.ofNumbers(Cartn_z),
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
id: CifField.ofColumn(Column.ofLambda({
value: row => row,
areValuesEqual: (rowA, rowB) => rowA === rowB,
rowCount, schema: d.id.schema,
})),
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
label_seq_id: CifField.ofNumbers(seq),
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
}
const categories = {
entity: CifCategory.ofTable('entity', db.entity),
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
}
return {
header: name,
categoryNames: Object.keys(categories),
categories
};
}
async function getCurve(name: string, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model)
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = MmcifFormat.fromFrame(cif)
const models = await createModels(format.data.db, format, ctx)
return models[0]
})
const curveModel = await curveModelTask.run()
return getStructure(curveModel)
}
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
const { name, source, results, nbCurve } = ingredient
// TODO can these be added to the library?
if (name === 'HIV1_CAhex_0_1_0') return
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
if (name === 'iLDL') return
if (name === 'peptides') return
if (name === 'lypoglycane') return
if (source.pdb === 'None') return
const model = await getModel(source.pdb || name, baseUrl)
if (!model) return
if (nbCurve) {
return getCurve(name, getCurveTransforms(ingredient), model)
} else {
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
return getAssembly(getResultTransforms(results), structure)
}
}
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing
const structures: Structure[] = []
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName)
const s = await getIngredientStructure(ingredients[iName], baseUrl)
if (s) structures.push(s)
}
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
const builder = Structure.Builder({ label: name })
let offsetInvariantId = 0
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
let maxInvariantId = 0
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId)
}
offsetInvariantId += maxInvariantId
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
const s = builder.getStructure()
return s
})
}
const RepresentationOptions = PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'] as const)
type RepresentationName = (typeof RepresentationOptions)[0][0]
export const LoadCellPackModel = StateAction.build({
display: { name: 'Load CellPack Model' },
params: {
id: PD.Select('influenza_model1.json', [
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
['influenza_model1.json', 'influenza_model1'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
['curveTest', 'Curve Test'],
] as const),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('spacefill', RepresentationOptions)
}, { isExpanded: true })
},
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
const url = getCellPackModelUrl(params.id, params.baseUrl)
const root = state.build().toRoot();
let cellPackBuilder: any
if (params.id === 'curveTest') {
const url = `${params.baseUrl}/extras/rna_allpoints.json`
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
const { points } = await (new Response(data)).json() as { points: number[] }
const curve0: Vec3[] = []
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j))
}
const cell: Cell = {
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
compartments: {
'CurveCompartment': {
interior: {
ingredients: {
'CurveIngredient': {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
}
}
}
}
}
}
cellPackBuilder = root
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
.apply(ParseCellPack)
} else {
cellPackBuilder = root
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack)
}
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
const { packings } = cellPackObject.data
const tree = state.build().to(cellPackBuilder.ref);
const isHiv = (
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
params.id === 'HIV-1_0.1.6-8_mixed_radii_pdb.cpr'
)
if (isHiv) {
for (let i = 0, il = packings.length; i < il; ++i) {
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
const url = `${params.baseUrl}/extras/rna_allpoints.json`
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
const { points } = await (new Response(data)).json() as { points: number[] }
const curve0: Vec3[] = []
for (let j = 0, jl = points.length; j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j))
}
packings[i].ingredients['RNA'] = {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
}
}
}
}
const colors = distinctColors(packings.length)
for (let i = 0, il = packings.length; i < il; ++i) {
const hcl = Hcl.fromColor(Hcl(), colors[i])
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number]
const p = { packing: i, baseUrl: params.baseUrl }
let cellpackTree = tree.apply(StructureFromCellpack, p)
if (params.preset.traceOnly) {
const expression = MS.struct.generator.atomGroups({
'atom-test': MS.core.logic.or([
MS.core.rel.eq([MS.ammp('label_atom_id'), 'CA']),
MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
])
})
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } }) as any
}
cellpackTree
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
color: getColorParams(hue)
})
)
}
if (isHiv) {
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
color: UniformColorThemeProvider
})
)
}
console.time('cellpack')
await state.updateTree(tree).runInContext(taskCtx);
console.timeEnd('cellpack')
}));
function getReprParams(ctx: PluginContext, params: { representation: RepresentationName, traceOnly: boolean }) {
const { representation, traceOnly } = params
switch (representation) {
case 'spacefill':
return traceOnly
? [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ sizeFactor: 2, ignoreHydrogens: true })
] as [any, any]
: [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ ignoreHydrogens: true })
] as [any, any]
case 'gaussian-surface':
return [
ctx.structureRepresentation.registry.get('gaussian-surface'),
() => ({
quality: 'custom', resolution: 10, radiusOffset: 2,
alpha: 1.0, flatShaded: false, doubleSided: false,
ignoreHydrogens: true
})
] as [any, any]
case 'point':
return [
ctx.structureRepresentation.registry.get('point'),
() => ({ ignoreHydrogens: true })
] as [any, any]
case 'ellipsoid':
return [
ctx.structureRepresentation.registry.get('orientation'),
() => ({})
] as [any, any]
}
}
function getColorParams(hue: [number, number]) {
return [
ModelIndexColorThemeProvider,
(c: ColorTheme.Provider<any>, ctx: ThemeRegistryContext) => {
return {
palette: {
name: 'generate',
params: {
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800,
maxCount: 75
}
}
}
}
] as [any, any]
}

View File

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

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 { StructureRepresentation3DHelpers } 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,
// StructureRepresentation3DHelpers.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,
// StructureRepresentation3DHelpers.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,14 +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/command';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginSpec } from '../../mol-plugin/spec';
import { LoadCellPackModel } from './extensions/cellpack/model';
import { StructureFromCellpack } from './extensions/cellpack/state';
require('mol-plugin-ui/skin/light.scss')
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
import { PluginConfig } from '../../mol-plugin/config';
import { CellPack } from '../../extensions/cellpack';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { Asset } from '../../mol-util/assets';
require('mol-plugin-ui/skin/light.scss');
function getParam(name: string, regex: string): string {
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
@@ -25,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: {
@@ -45,8 +50,10 @@ function init() {
},
config: DefaultPluginSpec.config
};
spec.config?.set(PluginConfig.Viewport.ShowExpand, false);
const plugin = createPlugin(document.getElementById('app')!, spec);
trySetSnapshot(plugin);
tryLoadFromUrl(plugin);
}
async function trySetSnapshot(ctx: PluginContext) {
@@ -58,11 +65,41 @@ async function trySetSnapshot(ctx: PluginContext) {
const url = snapshotId
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
: snapshotUrl;
await PluginCommands.State.Snapshots.Fetch.dispatch(ctx, { url })
await PluginCommands.State.Snapshots.Fetch(ctx, { url });
} catch (e) {
ctx.log.error('Failed to load snapshot.');
console.warn('Failed to load snapshot', e);
}
}
async function tryLoadFromUrl(ctx: PluginContext) {
const url = getParam('loadFromURL', '[^&]+').trim();
try {
if (!url) return;
let format = 'cif', isBinary = false;
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
case 'pdb': format = 'pdb'; break;
case 'mmbcif': isBinary = true; break;
}
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
url: Asset.Url(url),
format: format as any,
isBinary,
options: params.source.params.options,
}
}
}));
} catch (e) {
ctx.log.error(`Failed to load from URL (${url})`);
console.warn(`Failed to load from URL (${url})`, e);
}
}
init();

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,16 +91,16 @@ export function createProteopediaCustomTheme(colors: number[]) {
props,
description: Description,
legend: undefined
}
};
}
const ProteopediaCustomColorThemeProvider: ColorTheme.Provider<ProteopediaCustomColorThemeParams> = {
return {
name: 'proteopedia-custom',
label: 'Proteopedia Custom',
category: 'Custom',
factory: ProteopediaCustomColorTheme,
getParams: getChainIdColorThemeParams,
defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
}
return ProteopediaCustomColorThemeProvider;
};
}

View File

@@ -4,12 +4,11 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ResidueIndex, Model } from '../../mol-model/structure';
import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/registry';
import { BuiltInColorThemeName } from '../../mol-theme/color';
import { AminoAcidNames } from '../../mol-model/structure/model/types';
import { PluginContext } from '../../mol-plugin/context';
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';
export interface ModelInfo {
hetResidues: { name: string, indices: ResidueIndex[] }[],
@@ -54,14 +53,12 @@ export namespace ModelInfo {
const hetMap = new Map<string, ModelInfo['hetResidues'][0]>();
for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
if (AminoAcidNames.has(comp_id)) continue;
const mod_parent = model.properties.modifiedResidues.parentId.get(comp_id);
if (mod_parent && AminoAcidNames.has(mod_parent)) continue;
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);
let lig = hetMap.get(comp_id);
if (!lig) {
@@ -73,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,
@@ -87,6 +84,7 @@ export type SupportedFormats = 'cif' | 'pdb'
export interface LoadParams {
url: string,
format?: SupportedFormats,
isBinary?: boolean,
assemblyId?: string,
representationStyle?: RepresentationStyle
}
@@ -99,7 +97,7 @@ export interface RepresentationStyle {
}
export namespace RepresentationStyle {
export type Entry = { hide?: boolean, kind?: BuiltInStructureRepresentationsName, coloring?: BuiltInColorThemeName }
export type Entry = { hide?: boolean, kind?: StructureRepresentationRegistry.BuiltIn, coloring?: ColorTheme.BuiltIn }
}
export enum StateElements {

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,21 @@
$('assemblyId').onchange = function (e) { assemblyId = e.target.value; }
$('format').value = format;
$('format').onchange = function (e) { format = e.target.value; }
$('isBinary').checked = isBinary;
$('isBinary').onchange = function (e) { isBinary = !!e.target.checked; };
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
// var format = 'pdb';
// var assemblyId = 'deposited';
function loadAndSnapshot(params) {
PluginWrapper.load(params).then(() => {
setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);
});
}
var representationStyle = {
sequence: { coloring: 'proteopedia-custom' }, // or just { }
// sequence: { coloring: 'proteopedia-custom' }, // or just { }
hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
water: { hide: true },
snfg3d: { hide: false }
@@ -100,7 +109,7 @@
customColorList: CustomColors
});
PluginWrapper.setBackground(0xffffff);
PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId, representationStyle: representationStyle });
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.toggleSpin();
PluginWrapper.events.modelInfo.subscribe(function (info) {
@@ -108,8 +117,8 @@
listHetGroups(info);
});
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format }));
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
addSeparator();
@@ -172,16 +181,20 @@
addHeader('State');
var snapshot;
addControl('Create Snapshot', () => {
snapshot = PluginWrapper.snapshot.get();
// could use JSON.stringify(snapshot) and upload the data
addControl('Set Snapshot', () => {
// 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('Apply Snapshot', () => {
addControl('Restore Snapshot', () => {
if (!snapshot) return;
PluginWrapper.snapshot.set(snapshot);
// or download snapshot using fetch or ajax or whatever
// or PluginWrapper.snapshot.download(url);
});
addControl('Download State', () => {
PluginWrapper.snapshot.download('molj');
});
addControl('Download Session', () => {
PluginWrapper.snapshot.download('molx');
});
////////////////////////////////////////////////////////
@@ -217,7 +230,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

@@ -5,37 +5,33 @@
*/
import * as ReactDOM from 'react-dom';
import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/command';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { EvolutionaryConservation } from './annotation';
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
import { PluginState } from '../../mol-plugin/state';
import { Scheduler } from '../../mol-task';
import { createProteopediaCustomTheme } from './coloring';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
import { BuiltInColorThemes } from '../../mol-theme/color';
import { BuiltInSizeThemes } from '../../mol-theme/size';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
// import { Vec3 } from 'mol-math/linear-algebra';
// import { ParamDefinition } from 'mol-util/param-definition';
// import { Text } from 'mol-geo/geometry/text/text';
require('../../mol-plugin-ui/skin/light.scss')
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { EvolutionaryConservation } from './annotation';
import { createProteopediaCustomTheme } from './coloring';
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
import './index.html';
import { volumeStreamingControls } from './ui/controls';
require('../../mol-plugin-ui/skin/light.scss');
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 3;
static VERSION_MAJOR = 5;
static VERSION_MINOR = 4;
private _ev = RxEventHelper.create();
@@ -58,27 +54,27 @@ class MolStarProteopediaWrapper {
initial: {
isExpanded: false,
showControls: false
},
controls: {
right: ControlsWrapper
}
},
components: {
remoteState: 'none'
}
});
const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
this.plugin.representation.structure.themes.colorThemeRegistry.add(customColoring);
this.plugin.representation.structure.themes.colorThemeRegistry.add(EvolutionaryConservation.colorThemeProvider!);
this.plugin.managers.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
}
get state() {
return this.plugin.state.dataState;
return this.plugin.state.data;
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
private 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) {
@@ -92,10 +88,15 @@ class MolStarProteopediaWrapper {
private structure(assemblyId: string) {
const model = this.state.build().to(StateElements.Model);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
};
const s = model
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [EvolutionaryConservation.propertyProvider.descriptor.name], properties: {} }, { ref: StateElements.ModelProps, state: { isGhost: false } })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
.apply(StateTransforms.Model.StructureFromModel, props, { ref: StateElements.Assembly });
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: StateElements.Het });
@@ -118,9 +119,10 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.SequenceVisual);
} else {
root.applyOrUpdate(StateElements.SequenceVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.sequence && style.sequence.kind) || 'cartoon',
(style.sequence && style.sequence.coloring) || 'unit-index', structure));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.sequence && style.sequence.kind) || 'cartoon',
color: (style.sequence && style.sequence.coloring) || 'unit-index'
}));
}
}
@@ -133,9 +135,10 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.HetVisual);
} else {
root.applyOrUpdate(StateElements.HetVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
(style.hetGroups && style.hetGroups.coloring), structure));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
color: style.hetGroups && style.hetGroups.coloring
}));
}
}
}
@@ -149,7 +152,7 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.Het3DSNFG);
} else {
root.applyOrUpdate(StateElements.Het3DSNFG, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin, 'carbohydrate', void 0, structure));
createStructureRepresentationParams(this.plugin, structure, { type: 'carbohydrate' }));
}
}
}
@@ -160,9 +163,11 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.WaterVisual);
} else {
root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.water && style.water.kind) || 'ball-and-stick',
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.water && style.water.kind) || 'ball-and-stick',
typeParams: { alpha: 0.51 },
color: style.water && style.water.coloring
}));
}
}
@@ -180,20 +185,21 @@ 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;
}
private applyState(tree: StateBuilder) {
return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = '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.dataState;
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
@@ -202,8 +208,8 @@ class MolStarProteopediaWrapper {
}
if (loadType === 'full') {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
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;
@@ -213,39 +219,44 @@ class MolStarProteopediaWrapper {
const tree = state.build();
const info = await this.doInfo(true);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: asmId }));
const props = {
type: {
name: 'assembly' as const,
params: { id: asmId || 'deposited' }
}
};
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
await this.applyState(tree);
}
await this.updateStyle(representationStyle);
this.loadedParams = { url, format, assemblyId };
Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }));
}
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
const tree = this.visual(style, partial);
if (!tree) return;
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
setBackground(color: number) {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
}
viewport = {
setSettings: (settings?: Canvas3DProps) => {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: settings || DefaultCanvas3DParams
});
}
@@ -253,36 +264,17 @@ class MolStarProteopediaWrapper {
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset.dispatch(this.plugin, { }),
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
// if (!options) {
// PluginCommands.Canvas3D.SetSettings.dispatch(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.dispatch(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()
}
}
@@ -293,13 +285,8 @@ 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.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
if (!params || !!params.sequence) {
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
@@ -308,7 +295,7 @@ class MolStarProteopediaWrapper {
tree.to(StateElements.HetVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
await PluginCommands.State.Update(this.plugin, { state, tree });
}
}
@@ -328,7 +315,7 @@ class MolStarProteopediaWrapper {
remove: () => {
const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
if (!r) return;
PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
PluginCommands.State.RemoveObject(this.plugin, { state: this.state, ref: r.transform.ref });
if (this.experimentalDataElement) {
ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
this.experimentalDataElement = void 0;
@@ -339,14 +326,12 @@ class MolStarProteopediaWrapper {
hetGroups = {
reset: () => {
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
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.dispatch(this.plugin, { });
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
await PluginCommands.Camera.Reset(this.plugin, { });
const update = this.state.build();
@@ -361,65 +346,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 }
}));
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
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 snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius, radius);
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
const radius = Math.max(sphere.radius, 5);
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius);
PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: 250 });
}
}
private createSurVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
repr: BuiltInStructureRepresentations['ball-and-stick'],
color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
});
}
private createCoreVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
repr: BuiltInStructureRepresentations['ball-and-stick'],
// color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
// size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
});
}
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: async (url: string) => {
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, type: 'molj' | 'molx' = 'molj') => {
try {
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
await this.plugin.state.setSnapshot(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

@@ -6,27 +6,13 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PluginUIComponent } from '../../../mol-plugin-ui/base';
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin-ui/plugin';
import { AnimationControls } from '../../../mol-plugin-ui/state/animation';
import { CameraSnapshots } from '../../../mol-plugin-ui/camera';
import { PluginContext } from '../../../mol-plugin/context';
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
import { PluginContext } from '../../../mol-plugin/context';
import { StateElements } from '../helpers';
export class ControlsWrapper extends PluginUIComponent {
render() {
return <div className='msp-scrollable-container msp-right-controls'>
<CurrentObject />
<AnimationControls />
<CameraSnapshots />
</div>;
}
}
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={plugin}>
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
</PluginContextContainer>,
parent);
<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

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../mol-theme/theme';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Color } from '../../mol-util/color';
import { 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.';
export const CellPackColorThemeParams = {};
export type CellPackColorThemeParams = typeof CellPackColorThemeParams
export function getCellPackColorThemeParams(ctx: ThemeDataContext) {
return CellPackColorThemeParams; // TODO return copy
}
export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackColorThemeParams>): ColorTheme<CellPackColorThemeParams> {
let color: LocationColor;
let legend: ScaleLegend | TableLegend | undefined;
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
if (ctx.structure && info) {
const colors = distinctColors(info.packingsCount);
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;
let size = 0;
for (const m of models) size = Math.max(size, m.trajectoryInfo.size);
const palette = getPalette(size, { palette: {
name: 'generate',
params: {
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75,
minLabel: 'Min', maxLabel: 'Max', valueLabel: (i: number) => `${i + 1}`,
}
}});
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));
}
color = (location: Location): Color => {
if (StructureElement.Location.is(location)) {
return modelColor.get(location.unit.model.trajectoryInfo.index)!;
} else if (Bond.isLocation(location)) {
return modelColor.get(location.aUnit.model.trajectoryInfo.index)!;
}
return DefaultColor;
};
} else {
color = () => DefaultColor;
}
return {
factory: CellPackColorTheme,
granularity: 'instance',
color,
props,
description: Description,
legend
};
}
export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeParams, 'cellpack'> = {
name: 'cellpack',
label: 'CellPack',
category: ColorTheme.Category.Chain,
factory: CellPackColorTheme,
getParams: getCellPackColorThemeParams,
defaultValues: PD.getDefaultValues(CellPackColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => {
return (
!!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,551 @@
/**
* 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';
import { readFromFile } from '../../mol-util/data-source';
import { objectForEach } from '../../mol-util/object';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
}
class TrajectoryCache {
private map = new Map<string, Model.Trajectory>();
set(id: string, trajectory: Model.Trajectory) { this.map.set(id, trajectory); }
get(id: string) { return this.map.get(id); }
}
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
const assetManager = plugin.managers.asset;
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
let trajectory = trajCache.get(id);
let assets: Asset.Wrapper[] = [];
if (!trajectory) {
if (file) {
if (file.name.endsWith('.cif')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const cif = (await parseCif(plugin, text.data)).blocks[0];
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
} else if (file.name.endsWith('.bcif')) {
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
assets.push(binary);
const cif = (await parseCif(plugin, binary.data)).blocks[0];
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
} else if (file.name.endsWith('.pdb')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const pdb = await parsePDBfile(plugin, text.data, id);
trajectory = await plugin.runTask(trajectoryFromPDB(pdb));
} else {
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
const data = await getFromOPM(plugin, id, assetManager);
if (data.asset){
assets.push(data.asset);
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
}
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
}
} else {
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
assets.push(data.asset);
if ('pdb' in data) {
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
} else {
trajectory = await plugin.runTask(trajectoryFromMmCIF(data.mmcif));
}
}
trajCache.set(id, trajectory);
}
const model = trajectory[modelIndex];
return { model, assets };
}
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model);
const { assembly } = props;
if (assembly) {
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
}
let query;
if (source.selection){
const asymIds: string[] = source.selection.replace(' :', '').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, trajCache: TrajectoryCache) {
const { name, source, results, nbCurve } = ingredient;
if (source.pdb === 'None') return;
const file = ingredientFiles[source.pdb];
if (!file) {
// TODO can these be added to the library?
if (name === 'HIV1_CAhex_0_1_0') return;
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, trajCache, file);
if (!model) return;
let structure: Structure;
if (nbCurve) {
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
} else {
let bu: string|undefined = source.bu ? source.bu : undefined;
if (bu){
if (bu === 'AU') {
bu = undefined;
} else {
bu = bu.slice(2);
}
}
structure = await getStructure(plugin, model, source, { assembly: bu });
// transform with offset and pcp
let legacy: boolean = true;
if (ingredient.offset || ingredient.principalAxis){
legacy = false;
const structureMean = getStructureMean(structure);
Vec3.negate(structureMean, structureMean);
const m1: Mat4 = Mat4.identity();
Mat4.setTranslation(m1, structureMean);
structure = Structure.transform(structure, m1);
if (ingredient.offset){
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())){
const m: Mat4 = Mat4.identity();
Mat4.setTranslation(m, ingredient.offset);
structure = Structure.transform(structure, m);
}
}
if (ingredient.principalAxis){
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
const q: Quat = Quat.identity();
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
structure = Structure.transform(structure, m);
}
}
}
structure = getAssembly(getResultTransforms(results, legacy), structure);
}
return { structure, assets };
}
export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing;
const assets: Asset.Wrapper[] = [];
const trajCache = new TrajectoryCache();
const structures: Structure[] = [];
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
if (ingredientStructure) {
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
}
}
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) {
const ingredientFiles = params.ingredients.files || [];
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
if (params.source.name === 'id') {
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
} else {
const file = params.source.params;
if (!file?.file) {
plugin.log.error('No file selected');
return;
}
let jsonFile: Asset.File;
if (file.name.toLowerCase().endsWith('.zip')) {
const data = await readFromFile(file.file, 'zip').runInContext(runtime);
jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
objectForEach(data, (v, k) => {
if (k === 'model.json') return;
ingredientFiles.push(Asset.File(new File([v], k)));
});
} else {
jsonFile = file;
}
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
}
const cellPackBuilder = cellPackJson
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack);
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
const { packings } = cellPackObject.obj!.data;
await handleHivRna(plugin, packings, params.baseUrl);
for (let i = 0, il = packings.length; i < il; ++i) {
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles };
const packing = await state.build()
.to(cellPackBuilder.ref)
.apply(StructureFromCellpack, p)
.commit({ revertOnError: true });
const packingParams = {
traceOnly: params.preset.traceOnly,
representation: params.preset.representation,
};
await CellpackPackingPreset.apply(packing, packingParams, plugin);
if ( packings[i].location === 'surface' ){
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, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
}, { options: [['id', 'Id'], ['file', 'File']] }),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
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

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateObjectRef } from '../../mol-state';
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ColorNames } from '../../mol-util/color/names';
import { 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({
id: 'preset-structure-representation-cellpack-packing',
display: { name: 'CellPack Packing' },
params: () => CellpackPackingPresetParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
if (!structureCell) return {};
const reprProps = {
ignoreHydrogens: true,
traceOnly: params.traceOnly
};
const components = {
polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
};
if (params.representation === 'gaussian-surface') {
Object.assign(reprProps, {
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
});
} else if (params.representation === 'spacefill' && params.traceOnly) {
Object.assign(reprProps, { sizeFactor: 2 });
}
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
const color = CellPackColorThemeProvider.name;
const representations = {
polymer: builder.buildRepresentation<any>(update, components.polymer, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color }, { tag: 'polymer' })
};
await update.commit({ revertOnError: true });
return { components, representations };
}
});
//
export const CellpackMembranePresetParams = {
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
};
export type CellpackMembranePresetParams = PD.ValuesFor<typeof CellpackMembranePresetParams>
export const CellpackMembranePreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-cellpack-membrane',
display: { name: 'CellPack Membrane' },
params: () => CellpackMembranePresetParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
if (!structureCell) return {};
const reprProps = {
ignoreHydrogens: true,
};
const components = {
membrane: await presetStaticComponent(plugin, structureCell, 'all', { label: 'Membrane' })
};
if (params.representation === 'gaussian-surface') {
Object.assign(reprProps, {
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
});
}
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
const representations = {
membrane: builder.buildRepresentation(update, components.membrane, { type: 'gaussian-surface', typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
};
await update.commit({ revertOnError: true });
return { components, representations };
}
});

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
import { Structure, 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
packingIndex: number
}
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({
label: 'CellPack Info',
descriptor: CustomPropertyDescriptor({ name: 'cellpack-info' }),
type: 'root',
defaultParams: CellPackInfoParams,
getParams: (data: Structure) => CellPackInfoParams,
isApplicable: (data: Structure) => true,
obtain: async (ctx: CustomProperty.Context, data: Structure, props: CellPackInfoParams) => {
return {
value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
};
}
});

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

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
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',
category: 'custom-props',
display: {
name: 'Structure Quality Report',
description: 'Data from wwPDB Validation Report, obtained via PDBe.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = StructureQualityReportProvider
private labelPDBeValidation = {
label: (loci: Loci): string | undefined => {
if (!this.params.showTooltip) return void 0;
switch (loci.kind) {
case 'element-loci':
if (loci.elements.length === 0) return void 0;
const e = loci.elements[0];
const u = e.unit;
if (!u.model.customProperties.hasReference(StructureQualityReportProvider.descriptor)) return void 0;
const se = StructureElement.Location.create(loci.structure, u, u.elements[OrderedSet.getAt(e.indices, 0)]);
const issues = StructureQualityReport.getIssues(se);
if (issues.length === 0) return 'Validation: No Issues';
return `Validation: ${issues.join(', ')}`;
default: return void 0;
}
}
}
register(): void {
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.managers.lociLabels.addProvider(this.labelPDBeValidation);
this.ctx.representation.structure.themes.colorThemeRegistry.add(StructureQualityReportColorThemeProvider);
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
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);
return updated;
}
unregister() {
this.ctx.customModelProperties.unregister(StructureQualityReportProvider.descriptor.name);
this.ctx.managers.lociLabels.removeProvider(this.labelPDBeValidation);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(StructureQualityReportColorThemeProvider);
}
},
params: () => ({
autoAttach: PD.Boolean(false),
showTooltip: PD.Boolean(true)
})
});

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];
@@ -72,13 +72,15 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
granularity: 'group',
color: color,
props: props,
description: 'Assigns residue colors according to the number of issues or a specific issue in the PDBe Validation Report.',
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> = {
label: 'PDBe Structure Quality Report',
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
name: 'pdbe-structure-quality-report',
label: 'Structure Quality Report',
category: ColorTheme.Category.Validation,
factory: StructureQualityReportColorTheme,
getParams: ctx => {
const issueTypes = StructureQualityReport.getIssueTypes(ctx.structure);
@@ -101,7 +103,8 @@ export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Param
},
defaultValues: PD.getDefaultValues(StructureQualityReportColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => StructureQualityReport.isApplicable(ctx.structure?.models[0]),
ensureCustomProperties: (ctx: CustomProperty.Context, data: ThemeDataContext) => {
return data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0]) : Promise.resolve()
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(StructureQualityReportProvider.descriptor, false)
}
}
};

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,18 +105,18 @@ 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>
export const StructureQualityReportProvider: CustomModelProperty.Provider<StructureQualityReportParams, StructureQualityReport> = CustomModelProperty.createProvider({
label: 'PDBe Structure Quality Report',
label: 'Structure Quality Report',
descriptor: CustomPropertyDescriptor<ReportExportContext, any>({
name: 'pdbe_structure_quality_report',
cifExport: {
@@ -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

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { 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;
export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({
name: 'rcsb-assembly-symmetry-prop',
category: 'custom-props',
display: {
name: 'Assembly Symmetry',
description: 'Assembly Symmetry data calculated with BioJava, obtained via RCSB PDB.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
private provider = AssemblySymmetryProvider
register(): void {
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.genericRepresentationControls.set(Tag.Representation, selection => {
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);
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
}
unregister() {
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.genericRepresentationControls.delete(Tag.Representation);
this.ctx.customStructureControls.delete(Tag.Representation);
this.ctx.builders.structure.representation.unregisterPreset(AssemblySymmetryPreset);
}
},
params: () => ({
autoAttach: PD.Boolean(false),
serverUrl: PD.Text(AssemblySymmetry.DefaultServerUrl)
})
});
//
export const InitAssemblySymmetry3D = StateAction.build({
display: {
name: 'Assembly Symmetry',
description: 'Initialize Assembly Symmetry axes and cage. Data calculated with BioJava, obtained via RCSB PDB.'
},
from: PluginStateObject.Molecule.Structure,
isApplicable: (a) => AssemblySymmetry.isApplicable(a.data)
})(({ a, ref, state }, plugin: PluginContext) => Task.create('Init Assembly Symmetry', async ctx => {
try {
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;
}
const tree = state.build().to(ref)
.applyOrUpdateTagged(AssemblySymmetry.Tag.Representation, AssemblySymmetry3D);
await state.updateTree(tree).runInContext(ctx);
}));
export { AssemblySymmetry3D };
type AssemblySymmetry3D = typeof AssemblySymmetry3D
const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
name: Tag.Representation,
display: {
name: 'Assembly Symmetry',
description: 'Assembly Symmetry axes and cage. Data calculated with BioJava, obtained via RCSB PDB.'
},
from: PluginStateObject.Molecule.Structure,
to: PluginStateObject.Shape.Representation3D,
params: (a) => {
return {
...AssemblySymmetryParams,
};
}
})({
canAutoUpdate({ oldParams, newParams }) {
return true;
},
apply({ a, params }, plugin: PluginContext) {
return Task.create('Assembly Symmetry', async ctx => {
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);
await repr.createOrUpdate(params, a.data).runInContext(ctx);
const { type, kind, symbol } = assemblySymmetry;
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: kind, description: `${type} (${symbol})` });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
return Task.create('Assembly Symmetry', async ctx => {
await AssemblySymmetryProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
const assemblySymmetry = AssemblySymmetryProvider.get(a.data).value;
if (!assemblySymmetry || assemblySymmetry.symbol === 'C1') {
// this should NOT be StateTransformer.UpdateResult.Null
// because that keeps the old object
return StateTransformer.UpdateResult.Recreate;
}
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})`;
return StateTransformer.UpdateResult.Updated;
});
},
isApplicable(a) {
return AssemblySymmetry.isApplicable(a.data);
}
});
//
export const AssemblySymmetryPresetParams = {
...StructureRepresentationPresetProvider.CommonParams,
};
export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-rcsb-assembly-symmetry',
display: {
name: 'Assembly Symmetry', group: 'Annotation',
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);
},
params: () => AssemblySymmetryPresetParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
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 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 } };
}
});
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)
.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, getSymmetrySelectParam } 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({ scaleList: 'red-yellow-blue' }),
symmetryIndex: getSymmetrySelectParam(),
}
...getPaletteParams({ colorList: 'red-yellow-blue' }),
};
export type AssemblySymmetryClusterColorThemeParams = typeof AssemblySymmetryClusterColorThemeParams
export function getAssemblySymmetryClusterColorThemeParams(ctx: ThemeDataContext) {
const params = PD.clone(AssemblySymmetryClusterColorThemeParams)
params.symmetryIndex = getSymmetrySelectParam(ctx.structure)
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 { symmetryIndex } = props
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?.[symmetryIndex]?.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[]
if (operList.length === 0) operList.push('1') // TODO hack assuming '1' is the id of the identity operator
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);
}
}
}
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))
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 {
@@ -84,18 +84,21 @@ export function AssemblySymmetryClusterColorTheme(ctx: ThemeDataContext, props:
color,
props,
contextHash,
description: 'Assigns chain colors according to assembly symmetry cluster membership.',
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> = {
label: 'RCSB Assembly Symmetry Cluster',
export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<AssemblySymmetryClusterColorThemeParams, AssemblySymmetry.Tag.Cluster> = {
name: AssemblySymmetry.Tag.Cluster,
label: 'Assembly Symmetry Cluster',
category: ColorTheme.Category.Symmetry,
factory: AssemblySymmetryClusterColorTheme,
getParams: getAssemblySymmetryClusterColorThemeParams,
defaultValues: PD.getDefaultValues(AssemblySymmetryClusterColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => AssemblySymmetry.isApplicable(ctx.structure),
ensureCustomProperties: (ctx: CustomProperty.Context, data: ThemeDataContext) => {
return data.structure ? AssemblySymmetryProvider.attach(ctx, data.structure) : Promise.resolve()
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? AssemblySymmetryProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.customPropertyDescriptors.reference(AssemblySymmetryProvider.descriptor, false)
}
}
};

View File

@@ -0,0 +1,201 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
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, 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',
'author_defined_assembly',
'complete icosahedral assembly',
'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);
}
export namespace AssemblySymmetry {
export enum Tag {
Cluster = 'rcsb-assembly-symmetry-cluster',
Representation = 'rcsb-assembly-symmetry-3d'
}
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<CustomProperty.Data<AssemblySymmetryDataValue>> {
if (!isApplicable(structure)) return { value: [] };
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(ctx.runtime, query, variables);
let value: AssemblySymmetryDataValue = [];
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 { 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;
}
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']]);
if (structure) {
const assemblySymmetryData = AssemblySymmetryDataProvider.get(structure).value;
if (assemblySymmetryData) {
const options: [number, string][] = [];
for (let i = 0, il = assemblySymmetryData.length; i < il; ++i) {
const { symbol, kind } = assemblySymmetryData[i];
if (symbol !== 'C1') {
options.push([ i, `${i + 1}: ${symbol} ${kind}` ]);
}
}
if (options.length) {
param.options = options;
param.defaultValue = options[0][0];
}
}
}
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>
export type AssemblySymmetryDataValue = NonNullableArray<NonNullable<NonNullable<AssemblySymmetryQuery['assembly']>['rcsb_struct_symmetry']>>
export const AssemblySymmetryDataProvider: CustomStructureProperty.Provider<AssemblySymmetryDataParams, AssemblySymmetryDataValue> = CustomStructureProperty.createProvider({
label: 'Assembly Symmetry Data',
descriptor: CustomPropertyDescriptor({
name: 'rcsb_struct_symmetry_data',
// TODO `cifExport` and `symbol`
}),
type: 'root',
defaultParams: AssemblySymmetryDataParams,
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);
}
});
//
function getAssemblySymmetryParams(data?: Structure) {
return {
... AssemblySymmetryDataParams,
symmetryIndex: getSymmetrySelectParam(data)
};
}
export const AssemblySymmetryParams = getAssemblySymmetryParams();
export type AssemblySymmetryParams = typeof AssemblySymmetryParams
export type AssemblySymmetryProps = PD.Values<AssemblySymmetryParams>
export type AssemblySymmetryValue = AssemblySymmetryDataValue[0]
export const AssemblySymmetryProvider: CustomStructureProperty.Provider<AssemblySymmetryParams, AssemblySymmetryValue> = CustomStructureProperty.createProvider({
label: 'Assembly Symmetry',
descriptor: CustomPropertyDescriptor({
name: 'rcsb_struct_symmetry',
// TODO `cifExport` and `symbol`
}),
type: 'root',
defaultParams: AssemblySymmetryParams,
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 { value: assemblySymmetry };
}
});

View File

@@ -5,9 +5,9 @@
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { AssemblySymmetryValue, getSymmetrySelectParam, AssemblySymmetryProvider } 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';
@@ -29,31 +29,30 @@ import { TetrahedronCage } from '../../../mol-geo/primitive/tetrahedron';
import { IcosahedronCage } from '../../../mol-geo/primitive/icosahedron';
import { degToRad, radToDeg } from '../../../mol-math/misc';
import { Mutable } from '../../../mol-util/type-helpers';
import { ReadonlyVec3 } from '../../../mol-math/linear-algebra/3d/vec3';
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 }),
symmetryIndex: getSymmetrySelectParam(),
}
};
const AxesParams = {
...SharedParams,
@@ -63,126 +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>
//
type RotationAxes = ReadonlyArray<{ order: number, start: ReadonlyVec3, end: ReadonlyVec3 }>
function isRotationAxes(x: AssemblySymmetryValue[0]['rotation_axes']): x is RotationAxes {
return !!x && x.length > 0
}
function getAssemblyName(s: Structure) {
const { id } = s.units[0].conformation.operator.assembly
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 { symmetryIndex, scale } = props
const { scale } = props;
const { rotation_axes } = data[symmetryIndex]
if (!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[props.symmetryIndex]
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[props.symmetryIndex]
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);
}
//
@@ -190,144 +185,179 @@ 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: RotationAxes, size: number, structure: Structure) {
const eye = Vec3()
const target = Vec3()
const up = Vec3()
let pair: Mutable<RotationAxes> | undefined = undefined
function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.RotationAxes, size: number, structure: Structure) {
const eye = Vec3();
const target = Vec3();
const dir = 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
const d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
if (!pair[1] && (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);
Vec3.sub(dir, eye, target);
if (Vec3.dot(dir, up) < 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 { symmetryIndex, scale } = props
const assemblySymmetry = AssemblySymmetryProvider.get(data).value!;
const { scale } = props;
const { rotation_axes, symbol } = assemblySymmetry[symmetryIndex]
if (!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[props.symmetryIndex]
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('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

@@ -0,0 +1,158 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { 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
}
export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySymmetryControlState> {
protected defaultState(): AssemblySymmetryControlState {
return {
header: 'Assembly Symmetry',
isCollapsed: false,
isBusy: false,
isHidden: true,
brand: { accent: 'cyan', svg: Extension }
};
}
componentDidMount() {
this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, () => {
this.setState({
isHidden: !this.canEnable(),
description: StructureHierarchyManager.getSelectedStructuresDescription(this.plugin)
});
});
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 }));
}
get pivot() {
return this.plugin.managers.structure.hierarchy.selection.structures[0];
}
canEnable() {
const { selection } = this.plugin.managers.structure.hierarchy;
if (selection.structures.length !== 1) return false;
const pivot = this.pivot.cell;
if (!pivot.obj) return false;
return !!InitAssemblySymmetry3D.definition.isApplicable?.(pivot.obj, pivot.transform, this.plugin);
}
renderEnable() {
const pivot = this.pivot;
if (!pivot.cell.parent) return null;
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: Check }} />;
}
renderNoSymmetries() {
return <div className='msp-row-text'>
<div>No Symmetries for Assembly</div>
</div>;
}
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;
}
get values() {
const structure = this.pivot.cell.obj?.data;
if (structure) {
return AssemblySymmetryProvider.props(structure);
} else {
return { ...PD.getDefaultValues(AssemblySymmetryProvider.defaultParams), symmetryIndex: -1 };
}
}
async updateAssemblySymmetry(values: AssemblySymmetryProps) {
const s = this.pivot;
const currValues = AssemblySymmetryProvider.props(s.cell.obj!.data);
if (PD.areEqual(AssemblySymmetryProvider.defaultParams, currValues, values)) return;
if (s.properties) {
const b = this.plugin.state.data.build();
b.to(s.properties.cell).update(old => {
old.properties[AssemblySymmetryProvider.descriptor.name] = values;
});
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);
}
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);
}
get hasAssemblySymmetry3D() {
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;
}
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;
}
renderParams() {
return <>
<ParameterControls params={this.params} values={this.values} onChangeValues={this.paramsOnChange} />
</>;
}
renderControls() {
if (!this.pivot) return null;
if (this.noSymmetries) return this.renderNoSymmetries();
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.d.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!) {
}
}
}
`
`;

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,382 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
import { 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',
category: 'custom-props',
display: {
name: 'Validation Report',
description: 'Data from wwPDB Validation Report, obtained via RCSB PDB.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = ValidationReportProvider
private labelProvider = {
label: (loci: Loci): string | undefined => {
if (!this.params.showTooltip) return;
return [
geometryQualityLabel(loci),
densityFitLabel(loci),
randomCoilIndexLabel(loci)
].filter(l => !!l).join('</br>');
}
}
register(): void {
DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.managers.lociLabels.addProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(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.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;
this.params.autoAttach = p.autoAttach;
this.params.showTooltip = p.showTooltip;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
}
unregister() {
DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
this.ctx.managers.lociLabels.removeProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(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.builders.structure.representation.unregisterPreset(ValidationReportGeometryQualityPreset);
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportDensityFitPreset);
this.ctx.builders.structure.representation.unregisterPreset(ValidationReportRandomCoilIndexPreset);
}
},
params: () => ({
autoAttach: PD.Boolean(false),
showTooltip: PD.Boolean(true),
baseUrl: PD.Text(ValidationReport.DefaultBaseUrl)
})
});
//
function geometryQualityLabel(loci: Loci): string | undefined {
if (loci.kind === 'element-loci') {
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 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 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));
if (issues.size === 0) {
return `Geometry Quality <small>(1 Atom)</small>: no issues`;
}
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>();
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 { geometryIssues } = validationReport;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const { elements } = unit;
OrderedSet.forEach(indices, idx => {
const eI = elements[idx];
const rI = residueIndex[eI];
const residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const issues = geometryIssues.get(rI);
if (issues) {
issues.forEach(name => {
const count = cummulativeIssues.get(name) || 0;
cummulativeIssues.set(name, count + 1);
});
}
seen.add(residueKey);
}
});
}
if (!hasValidationReport) return;
const residueCount = `<small>(${seen.size} ${seen.size > 1 ? 'Residues' : 'Residue'})</small>`;
if (cummulativeIssues.size === 0) {
return `Geometry Quality ${residueCount}: no issues`;
}
const summary: string[] = [];
cummulativeIssues.forEach((count, name) => {
summary.push(`${name}${count > 1 ? ` \u00D7 ${count}` : ''}`);
});
return `Geometry Quality ${residueCount}: ${summary.join(', ')}`;
}
}
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;
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 { 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 residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const rsrzValue = rsrz.get(rI);
const rsccValue = rscc.get(rI);
if (rsrzValue !== undefined) {
rsrzSum += rsrzValue;
rsrzSeen.add(residueKey);
} else if (rsccValue !== undefined) {
rsccSum += rsccValue;
rsccSeen.add(residueKey);
}
seen.add(residueKey);
}
});
}
if (seen.size === 0) return;
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)}`);
}
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)}`);
}
if (summary.length) {
return summary.join('</br>');
}
}
}
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;
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 { 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 residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const rciValue = rci.get(rI);
if (rciValue !== undefined) {
sum += rciValue;
seen.add(residueKey);
}
}
});
}
if (seen.size === 0) return;
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)}`;
}
}
//
const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modifier.union([
MS.struct.modifier.wholeResidues([
MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
'atom-test': ValidationReport.symbols.hasClash.symbol(),
})
])
])
]), {
description: 'Select residues with clashes in the wwPDB validation report.',
category: StructureSelectionCategory.Residue,
ensureCustomProperties: (ctx, structure) => {
return ValidationReportProvider.attach(ctx, structure.models[0]);
}
});
//
export const ValidationReportGeometryQualityPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-rcsb-validation-report-geometry-uality',
display: {
name: 'Validation Report (Geometry Quality)', group: 'Annotation',
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]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const model = structureCell?.obj?.data.model;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
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 clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' });
const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
let clashesBallAndStick, clashesSnfg3d;
if (representations) {
clashesBallAndStick = builder.buildRepresentation(update, clashes, { type: 'ball-and-stick', typeParams, color: colorTheme }, { tag: 'clashes-ball-and-stick' });
clashesSnfg3d = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-snfg-3d' });
}
await update.commit({ revertOnError: true });
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } };
}
});
export const ValidationReportDensityFitPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-rcsb-validation-report-density-fit',
display: {
name: 'Validation Report (Density Fit)', group: 'Annotation',
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.isFromXray(a.data.models[0]) && Model.probablyHasDensityMap(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;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
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);
}
});
export const ValidationReportRandomCoilIndexPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-rcsb-validation-report-random-coil-index',
display: {
name: 'Validation Report (Random Coil Index)', group: 'Annotation',
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]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const model = structureCell?.obj?.data.model;
if (!structureCell || !model) return {};
await plugin.runTask(Task.create('Validation Report', async runtime => {
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);
}
});

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { 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);
export function DensityFitColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
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];
if (validationReport?.value && model) {
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;
}
return DefaultColor;
};
}
return {
factory: DensityFitColorTheme,
granularity: 'group',
color,
props,
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> = {
name: ValidationReport.Tag.DensityFit,
label: 'Density Fit',
category: ColorTheme.Category.Validation,
factory: DensityFitColorTheme,
getParams: () => ({}),
defaultValues: PD.getDefaultValues({}),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.isFromXray(ctx.structure.models[0]) && Model.probablyHasDensityMap(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

@@ -0,0 +1,115 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { 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 NoIssuesColor = Color(0x2166ac);
const OneIssueColor = Color(0xfee08b);
const TwoIssuesColor = Color(0xf46d43);
const ThreeOrMoreIssuesColor = Color(0xa50026);
const ColorLegend = TableLegend([
['Data unavailable', DefaultColor],
['No issues', NoIssuesColor],
['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][] = [];
if (validationReport) {
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;
const validationReport = ctx.structure && ValidationReportProvider.get(ctx.structure.models[0]);
const contextHash = validationReport?.version;
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);
color = (location: Location): Color => {
if (StructureElement.Location.is(location) && location.unit.model === model) {
const { element } = location;
const rI = residueIndex[element];
const value = geometryIssues.get(rI);
if (value === undefined) return DefaultColor;
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;
}
switch (count) {
case undefined: return DefaultColor;
case 0: return NoIssuesColor;
case 1: return OneIssueColor;
case 2: return TwoIssuesColor;
default: return ThreeOrMoreIssuesColor;
}
}
return DefaultColor;
};
}
return {
factory: GeometryQualityColorTheme,
granularity: 'group',
color,
props,
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> = {
name: ValidationReport.Tag.GeometryQuality,
label: 'Geometry Quality',
category: ColorTheme.Category.Validation,
factory: GeometryQualityColorTheme,
getParams: getGeometricQualityColorThemeParams,
defaultValues: PD.getDefaultValues(getGeometricQualityColorThemeParams({})),
isApplicable: (ctx: ThemeDataContext) => ValidationReport.isApplicable(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

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { 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);
export function RandomCoilIndexColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
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 rci = validationReport?.value?.rci;
const model = ctx.structure?.models[0];
if (rci && model) {
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);
}
return DefaultColor;
};
}
return {
factory: RandomCoilIndexColorTheme,
granularity: 'group',
color,
props,
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> = {
name: ValidationReport.Tag.RandomCoilIndex,
label: 'Random Coil Index',
category: ColorTheme.Category.Validation,
factory: RandomCoilIndexColorTheme,
getParams: () => ({}),
defaultValues: PD.getDefaultValues({}),
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,23 +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 { 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 {
/**
@@ -82,52 +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;
}
),
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 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 = {
@@ -135,25 +152,25 @@ 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>
export const ValidationReportProvider: CustomModelProperty.Provider<ValidationReportParams, ValidationReport> = CustomModelProperty.createProvider({
label: 'RCSB Validation Report',
label: 'Validation Report',
descriptor: CustomPropertyDescriptor({
name: 'rcsb_validation_report',
// TODO `cifExport` and `symbol`
symbols: ValidationReport.symbols
}),
type: 'dynamic',
defaultParams: ValidationReportParams,
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);
}
})
});
//
@@ -177,26 +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()
const 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)) {
@@ -204,76 +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 { elements } = unit
const { a, b, edgeCount, edgeProps } = clashes
const pA = Vec3(), pB = Vec3();
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) {
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])
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]);
}
}
}
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({
@@ -287,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) {
@@ -311,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) {
@@ -320,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

@@ -14,47 +14,47 @@ 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 } from '../../../mol-repr/structure/representation';
import { UnitKind, UnitKindOptions } from '../../../mol-repr/structure/visual/util/common';
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 } 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 = {
@@ -62,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> {
@@ -79,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 = {
@@ -175,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> {
@@ -192,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);
}
//
@@ -258,36 +258,38 @@ 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: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
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<ClashesParams> = {
label: 'RCSB Clashes',
description: 'Displays clashes between atoms as disks.',
export const ClashesRepresentationProvider = StructureRepresentationProvider({
name: ValidationReport.Tag.Clashes,
label: 'Validation Clashes',
description: 'Displays clashes between atoms as disks. Data from wwPDB Validation Report, obtained via RCSB PDB.',
factory: ClashesRepresentation,
getParams: getClashesParams,
defaultValues: PD.getDefaultValues(ClashesParams),
defaultColorTheme: { name: 'uniform', props: { value: Color(0xFA28FF) } },
defaultSizeTheme: { name: 'physical' },
isApplicable: (structure: Structure) => structure.elementCount > 0,
ensureCustomProperties: (ctx: CustomProperty.Context, structure: Structure) => {
return ClashesProvider.attach(ctx, structure)
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, structure: Structure) => ClashesProvider.attach(ctx, structure, void 0, true),
detach: (data) => ClashesProvider.ref(data, false)
}
}
});

View File

@@ -1,15 +1,16 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { 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();
@@ -33,6 +34,7 @@ class Camera {
zoom = 1
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
readonly stateChanged = new BehaviorSubject<Partial<Camera.Snapshot>>(this.state);
get position() { return this.state.position; }
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
@@ -45,8 +47,8 @@ class Camera {
private prevProjection = Mat4.identity();
private prevView = Mat4.identity();
private deltaDirection = Vec3.zero();
private newPosition = Vec3.zero();
private deltaDirection = Vec3();
private newPosition = Vec3();
update() {
const snapshot = this.state as Camera.Snapshot;
@@ -64,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);
@@ -76,53 +78,58 @@ class Camera {
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
this.transition.apply(snapshot, durationMs);
this.stateChanged.next(snapshot);
}
getSnapshot() {
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
}
getFocus(target: Vec3, radiusNear: number, radiusFar: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
const fov = this.state.fov
const { width, height } = this.viewport
const aspect = width / height
const aspectFactor = (height < width ? 1 : aspect)
const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
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.radiusNear = radiusNear
state.radiusFar = radiusFar
state.position = Vec3.clone(this.newPosition)
if (up) Vec3.matchDirection(state.up, up, state.up)
return state
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));
}
focus(target: Vec3, radiusNear: number, radiusFar: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radiusNear > 0 && radiusFar > 0) {
this.setState(this.getFocus(target, radiusNear, radiusFar, up, dir), durationMs);
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
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);
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
state.target = Vec3.clone(target);
state.radius = r;
state.position = Vec3.clone(this.newPosition);
if (up) Vec3.matchDirection(state.up, up, state.up);
return state;
}
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radius > 0) {
this.setState(this.getFocus(target, radius, up, dir), durationMs);
}
}
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)) {
this.viewport = viewport;
Camera.copySnapshot(this.state, state);
}
}
namespace Camera {
@@ -144,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 {
@@ -161,8 +168,8 @@ namespace Camera {
up: Vec3.create(0, 1, 0),
target: Vec3.create(0, 0, 0),
radiusNear: 10,
radiusFar: 10,
radius: 10,
radiusMax: 10,
fog: 50,
clipFar: true
};
@@ -176,8 +183,8 @@ namespace Camera {
up: Vec3
target: Vec3
radiusNear: number
radiusFar: number
radius: number
radiusMax: number
fog: number
clipFar: boolean
}
@@ -192,8 +199,8 @@ namespace Camera {
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
if (typeof source.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
if (typeof source.radius !== 'undefined') out.radius = source.radius;
if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
if (typeof source.fog !== 'undefined') out.fog = source.fog;
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
@@ -202,83 +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) {
const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
let { radius, radiusMax, mode, fog, clipFar } = camera.state;
if (radius < 0.01) radius = 0.01;
const cDist = Vec3.distance(camera.position, camera.target)
let near = cDist - radiusNear
let far = cDist + (clipFar ? radiusNear : radiusFar)
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 = cDist - (radiusNear * 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;
}
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;
@@ -17,24 +17,40 @@ class CameraTransitionManager {
private start = 0;
inTransition = false;
private durationMs = 0;
private source: Camera.Snapshot = Camera.createDefaultSnapshot();
private target: Camera.Snapshot = Camera.createDefaultSnapshot();
private current = Camera.createDefaultSnapshot();
private _source: Camera.Snapshot = Camera.createDefaultSnapshot();
private _target: Camera.Snapshot = Camera.createDefaultSnapshot();
private _current = Camera.createDefaultSnapshot();
get source(): Readonly<Camera.Snapshot> { return this._source; }
get target(): Readonly<Camera.Snapshot> { return this._target; }
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
this.finish(to);
if (!this.inTransition || durationMs > 0) {
Camera.copySnapshot(this._source, this.camera.state);
}
if (!this.inTransition) {
Camera.copySnapshot(this._target, this.camera.state);
}
Camera.copySnapshot(this._target, to);
if (this._target.radius > this._target.radiusMax) {
this._target.radius = this._target.radiusMax;
}
if (!this.inTransition && durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
this.finish(this._target);
return;
}
Camera.copySnapshot(this.source, this.camera.state);
Camera.copySnapshot(this.target, this.camera.state);
Camera.copySnapshot(this.target, to);
this.inTransition = true;
this.func = transition || CameraTransitionManager.defaultTransition;
this.start = this.t;
this.durationMs = durationMs;
if (!this.inTransition || durationMs > 0) {
this.start = this.t;
this.durationMs = durationMs;
}
}
tick(t: number) {
@@ -52,12 +68,12 @@ class CameraTransitionManager {
const normalized = Math.min((this.t - this.start) / this.durationMs, 1);
if (normalized === 1) {
this.finish(this.target!);
this.finish(this._target!);
return;
}
this.func(this.current, normalized, this.source, this.target);
Camera.copySnapshot(this.camera.state, this.current);
this.func(this._current, normalized, this._source, this._target);
Camera.copySnapshot(this.camera.state, this._current);
}
constructor(private camera: Camera) {
@@ -79,9 +95,9 @@ namespace CameraTransitionManager {
// Lerp target, position & radius
Vec3.lerp(out.target, source.target, target.target, t);
Vec3.lerp(out.position, source.position, target.position, t);
out.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
out.radius = lerp(source.radius, target.radius, t);
// TODO take change of `clipFar` into account
out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
out.radiusMax = lerp(source.radiusMax, target.radiusMax, t);
// Lerp fov & fog
out.fov = lerp(source.fov, target.fov, t);

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';
@@ -31,15 +31,28 @@ import { PixelData } from '../mol-util/image';
import { readTexture } from '../mol-gl/compute/util';
import { DrawPass } from './passes/draw';
import { PickPass } from './passes/pick';
import { Task } from '../mol-task';
import { ImagePass, ImageProps } from './passes/image';
import { Sphere3D } from '../mol-math/geometry';
import { isDebugMode } from '../mol-util/debug';
import { CameraHelperParams } from './helper/camera-helper';
import { produce } from 'immer';
export const Canvas3DParams = {
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
cameraClipFar: PD.Boolean(true),
camera: PD.Group({
mode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']] as const, { label: 'Camera' }),
helper: PD.Group(CameraHelperParams, { isFlat: true })
}, { pivot: 'mode' }),
cameraFog: PD.MappedStatic('on', {
on: PD.Group({
intensity: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Show fog in the distance' }),
cameraClipping: PD.Group({
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
}, { pivot: 'radius' }),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
transparentBackground: PD.Boolean(false),
@@ -48,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,
@@ -65,6 +78,7 @@ interface Canvas3D {
commit(isSynchronous?: boolean): void
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
clear(): void
syncVisibility(): void
requestDraw(force?: boolean): void
animate(): void
@@ -77,13 +91,12 @@ interface Canvas3D {
handleResize(): void
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
requestCameraReset(durationMs?: number): void
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
getImagePass(): ImagePass
setProps(props: Partial<Canvas3DProps> | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void)): void
getImagePass(props: Partial<ImageProps>): ImagePass
/** Returns a copy of the current Canvas3D instance props */
readonly props: Readonly<Canvas3DProps>
@@ -94,155 +107,152 @@ interface Canvas3D {
dispose(): void
}
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
const DefaultRunTask = (task: Task<unknown>) => task.run()
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 }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}) {
const gl = getGLContext(canvas, {
alpha: true,
antialias: true,
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()
if (isDebugMode) console.log('lose context');
loseContextExt.loseContext();
setTimeout(() => {
if (!webgl.isContextLost) return
console.log('restore context')
loseContextExt.restoreContext()
}, 1000)
}, false)
if (!webgl.isContextLost) return;
if (isDebugMode) 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, runTask)
return Canvas3D.create(webgl, input, props);
}
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
const p = { ...DefaultCanvas3DParams, ...props }
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}): Canvas3D {
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.cameraMode,
fog: p.cameraFog,
clipFar: p.cameraClipFar
})
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)
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 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 contextRestoredSub = contextRestored.subscribe(() => {
pickPass.pickDirty = true
draw(true)
})
let drawPending = false
let cameraResetRequested = false
let nextCameraResetDuration: number | 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;
@@ -253,34 +263,32 @@ 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;
}
function animate() {
currentTime = now();
commit();
camera.transition.tick(currentTime);
draw(false);
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) {
@@ -291,52 +299,95 @@ namespace Canvas3D {
function resolveCameraReset() {
if (!cameraResetRequested) return;
const { center, radius } = scene.boundingSphere;
camera.focus(center, radius, radius,
typeof nextCameraResetDuration === 'undefined' ? p.cameraResetDurationMs : nextCameraResetDuration);
const { center, radius } = scene.boundingSphereVisible;
if (radius > 0) {
const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration;
const focus = camera.getFocus(center, radius);
const snapshot = nextCameraResetSnapshot ? { ...focus, ...nextCameraResetSnapshot } : focus;
camera.setState(snapshot, duration);
}
nextCameraResetDuration = void 0;
nextCameraResetSnapshot = void 0;
cameraResetRequested = false;
}
const oldBoundingSphereVisible = Sphere3D();
const cameraSphere = Sphere3D();
function shouldResetCamera() {
if (camera.state.radiusMax === 0) return true;
if (camera.transition.inTransition || nextCameraResetSnapshot) return false;
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
for (const r of scene.renderables) {
if (!r.state.visible) continue;
const b = r.values.boundingSphere.ref.value;
if (!b.radius) continue;
if (!Sphere3D.includes(oldBoundingSphereVisible, b)) return true;
if (Sphere3D.overlaps(cameraSphere, b)) cameraSphereOverlapsNone = false;
}
return cameraSphereOverlapsNone;
}
const sceneCommitTimeoutMs = 250;
function commitScene(isSynchronous: boolean) {
if (!scene.needsCommit) return true;
// snapshot the current bounding sphere of visible objects
Sphere3D.copy(oldBoundingSphereVisible, scene.boundingSphereVisible);
if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false;
if (debugHelper.isEnabled) debugHelper.update();
if (reprCount.value === 0 || shouldResetCamera()) {
cameraResetRequested = true;
}
if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
reprCount.next(reprRenderObjects.size);
return true;
}
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);
}
}
@@ -345,7 +396,7 @@ namespace Canvas3D {
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
if (!repr.state.syncManually) add(repr);
}))
}));
}
function unregisterAutoUpdate(repr: Representation.Any) {
@@ -356,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,
@@ -369,17 +450,27 @@ 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;
}
if (scene.syncVisibility()) {
if (debugHelper.isEnabled) debugHelper.update();
}
},
// draw,
@@ -390,55 +481,81 @@ namespace Canvas3D {
getLoci,
handleResize,
requestCameraReset: (durationMs) => {
nextCameraResetDuration = durationMs;
requestCameraReset: options => {
nextCameraResetDuration = options?.durationMs;
nextCameraResetSnapshot = options?.snapshot;
cameraResetRequested = true;
},
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>) => {
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
camera.setState({ mode: props.cameraMode })
}
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
camera.setState({ fog: props.cameraFog })
}
if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
camera.setState({ clipFar: props.cameraClipFar })
}
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
setProps: (properties) => {
const props: Partial<Canvas3DProps> = typeof properties === 'function'
? produce(getProps(), properties)
: properties;
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)
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;
}
if (props.cameraFog !== undefined) {
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
if (newFog !== camera.state.fog) cameraState.fog = newFog;
}
if (props.cameraClipping !== undefined) {
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
cameraState.clipFar = props.cameraClipping.far;
}
if (props.cameraClipping.radius !== undefined) {
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
if (radius > 0 && radius !== cameraState.radius) {
// if radius = 0, NaNs happen
cameraState.radius = Math.max(radius, 0.01);
}
}
}
if (Object.keys(cameraState).length > 0) camera.setState(cameraState);
if (props.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);
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;
return {
cameraMode: camera.state.mode,
cameraFog: camera.state.fog,
cameraClipFar: camera.state.clipFar,
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,
@@ -447,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,22 +16,22 @@ 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 the 3D scene by dragging using ${triggers}'),
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate the 3D scene around the z-axis by dragging using ${triggers}'),
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan the 3D scene by dragging using ${triggers}'),
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate', 'Drag using ${triggers}'),
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate around z-axis', 'Drag using ${triggers}'),
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan', 'Drag using ${triggers}'),
dragZoom: Binding.Empty,
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus the 3D scene by dragging using ${triggers}'),
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom the 3D scene by dragging using ${triggers}'),
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus', 'Drag using ${triggers}'),
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom', 'Drag using ${triggers}'),
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom the 3D scene by scrolling using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Focus the 3D scene by scrolling using ${triggers}'),
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom', 'Scroll using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'),
scrollFocusZoom: Binding.Empty,
}
};
export const TrackballControlsParams = {
noScroll: PD.Boolean(true, { isHidden: true }),
@@ -41,7 +41,7 @@ export const TrackballControlsParams = {
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
spinSpeed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
staticMoving: PD.Boolean(true, { isHidden: true }),
dynamicDampingFactor: PD.Numeric(0.2, {}, { 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,143 +125,147 @@ 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 radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
camera.setState({ radiusNear })
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);
}
}
}
/** Ensure the distance between object and target is within the min/max distance */
/**
* Ensure the distance between object and target is within the min/max distance
* and not too large compared to `camera.state.radiusMax`
*/
function checkDistances() {
if (Vec3.squaredMagnitude(_eye) > p.maxDistance * p.maxDistance) {
Vec3.setMagnitude(_eye, _eye, p.maxDistance)
Vec3.add(camera.position, camera.target, _eye)
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_focusStart, _focusEnd)
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);
}
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);
}
}
@@ -271,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;
@@ -291,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
@@ -304,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({ radiusNear: dist / 5 })
camera.setState({ radius: dist / 5 });
}
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2);
}
function onInteractionEnd() {
@@ -353,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);
@@ -392,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

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { 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';
@@ -20,10 +20,11 @@ import { ValueCell } from '../../mol-util';
import { Geometry } from '../../mol-geo/geometry/geometry';
export const DebugHelperParams = {
sceneBoundingSpheres: PD.Boolean(false, { description: 'Show scene bounding spheres.' }),
objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
instanceBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of instances.' }),
}
sceneBoundingSpheres: PD.Boolean(false, { description: 'Show full scene bounding spheres.' }),
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>
@@ -37,108 +38,126 @@ export class BoundingSphereHelper {
private objectsData = new Map<GraphicsRenderObject, BoundingSphereData>()
private instancesData = new Map<GraphicsRenderObject, BoundingSphereData>()
private sceneData: BoundingSphereData | undefined
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.grey)
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;
this.parent.forEach((r, ro) => {
const objectData = this.objectsData.get(ro)
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato)
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);
if (ro.type === 'mesh' || ro.type === 'lines' || ro.type === 'points') {
const instanceData = this.instancesData.get(ro)
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, {
aTransform: ro.values.aTransform,
matrix: ro.values.matrix,
transform: ro.values.transform,
extraTransform: ro.values.extraTransform,
uInstanceCount: ro.values.uInstanceCount,
instanceCount: ro.values.instanceCount,
aInstance: ro.values.aInstance,
})
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
}
})
const instanceData = this.instancesData.get(ro);
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, instanceMaterialId, {
aTransform: ro.values.aTransform,
matrix: ro.values.matrix,
transform: ro.values.transform,
extraTransform: ro.values.extraTransform,
uInstanceCount: ro.values.uInstanceCount,
instanceCount: ro.values.instanceCount,
aInstance: ro.values.aInstance,
});
if (newInstanceData) this.instancesData.set(ro, newInstanceData);
});
this.objectsData.forEach((objectData, ro) => {
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.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.objectBoundingSpheres || this._props.instanceBoundingSpheres
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, transform?: TransformData) {
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, materialId: number, transform?: TransformData) {
if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) {
const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh)
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, transform)
const 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)
if (boundingSphere.radius) addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
return MeshBuilder.getMesh(builderState)
const detail = 2;
const vertexCount = sphereVertexCount(detail);
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh);
if (boundingSphere.radius) {
addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail);
if (Sphere3D.hasExtrema(boundingSphere)) {
for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0);
}
}
return MeshBuilder.getMesh(builderState);
}
const boundingSphereHelberMaterialId = getNextMaterialId()
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, transform?: TransformData) {
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, boundingSphereHelberMaterialId)
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, writeDepth: false }, materialId);
}

View File

@@ -0,0 +1,175 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
import Scene from '../../mol-gl/scene';
import { Camera } from '../camera';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
import { GraphicsRenderObject } from '../../mol-gl/render-object';
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
import { ColorNames } from '../../mol-util/color/names';
import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
import { Viewport } from '../camera/util';
import { ValueCell } from '../../mol-util';
import { Sphere3D } from '../../mol-math/geometry';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import produce from 'immer';
import { Shape } from '../../mol-model/shape';
// TODO add scale line/grid
const AxesParams = {
...Mesh.Params,
alpha: { ...Mesh.Params.alpha, defaultValue: 0.33 },
ignoreLight: { ...Mesh.Params.ignoreLight, defaultValue: true },
colorX: PD.Color(ColorNames.red, { isEssential: true }),
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>
export const CameraHelperParams = {
axes: PD.MappedStatic('on', {
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>
export class CameraHelper {
scene: Scene
camera: Camera
props: CameraHelperProps = {
axes: { name: 'off', params: {} }
}
private renderObject: GraphicsRenderObject | undefined
constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
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.setProps(props);
}
setProps(props: Partial<CameraHelperProps>) {
this.props = produce(this.props, p => {
if (props.axes !== undefined) {
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();
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 };
}
}
});
}
get isEnabled() {
return this.props.axes.name === 'on';
}
update(camera: Camera) {
if (!this.renderObject) return;
updateCamera(this.camera, camera.viewport);
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;
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);
}
}
function updateCamera(camera: Camera, viewport: Viewport) {
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 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;
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 };
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 = 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);
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 getColor = (groupId: number) => {
switch (groupId) {
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, () => '');
}
function createAxesRenderObject(props: AxesProps) {
const shape = getAxesShape(props);
return Shape.createRenderObject(shape, props);
}

View File

@@ -111,7 +111,7 @@ export class Canvas3dInteractionHelper {
this.ev.dispose();
}
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 30) {
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
if (!inside) return;
this.move(x, y, buttons, button, modifiers);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -11,65 +11,93 @@ import Scene from '../../mol-gl/scene';
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
import { Texture } from '../../mol-gl/webgl/texture';
import { Camera } from '../camera';
import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
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>
export class DrawPass {
colorTarget: RenderTarget
depthTexture: Texture
packedDepth: boolean
cameraHelper: CameraHelper
private depthTarget: RenderTarget | null
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
const { gl, extensions, resources } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
this.colorTarget = 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')
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');
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 };
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);
}
get props(): DrawPassProps {
return {
cameraHelper: { ...this.cameraHelper.props }
};
}
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { webgl, renderer, scene, camera, debugHelper, 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())
renderer.render(scene, camera, 'color', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, camera, 'color', false, 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()
renderer.render(scene, camera, 'depth', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, camera, 'depth', false, 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);
if (debugHelper.isEnabled) {
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);
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -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 } 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';
@@ -20,7 +20,8 @@ export const ImageParams = {
transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
}
drawPass: PD.Group(DrawPassParams),
};
export type ImageProps = PD.Values<typeof ImageParams>
export class ImagePass {
@@ -30,69 +31,79 @@ 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)
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.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 {
return {
transparentBackground: this._transparentBackground,
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(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;
});
});

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