Compare commits

...

134 Commits

Author SHA1 Message Date
Alexander Rose
3415fe0847 0.7.1-dev.2 2020-05-09 11:54:33 -07:00
Alexander Rose
1569958a29 debug-mode url param, canvas3d console stats 2020-05-09 11:52:09 -07:00
Alexander Rose
3543faa0c2 fix snapshot loading 2020-05-09 11:03:58 -07:00
Alexander Rose
251dbf3877 0.7.1-dev.1 2020-05-08 15:30:57 -07:00
Alexander Rose
32d35efef0 add grid size to volume transform description and incr version 2020-05-08 15:27:39 -07:00
Alexander Rose
8b6428a61d 0.7.0 2020-05-08 10:45:43 -07:00
Alexander Rose
dda43370cf add setProductionMode, setDebugMode exports 2020-05-08 10:42:38 -07:00
Alexander Rose
92c1e979c0 add version to viewer module export 2020-05-08 10:36:26 -07:00
Alexander Rose
ad38a33943 basic support for aligning coarse structures 2020-05-08 10:20:09 -07:00
Alexander Rose
88c276a4c7 inline option for ToggleSelectionModeButton 2020-05-07 16:16:32 -07:00
Alexander Rose
0a3d19235d 0.7.0-dev.21 2020-05-06 20:16:36 -07:00
Alexander Rose
0d90fd1f06 tweak version script
- so the build contains the correct version number
2020-05-06 20:14:52 -07:00
Alexander Rose
02d3274e83 0.7.0-dev.20 2020-05-06 18:49:10 -07:00
Alexander Rose
2531af2b94 use cursor icon for selection mode 2020-05-06 18:02:53 -07:00
Alexander Rose
850328be4e fix coloring of bonds via overpaint 2020-05-06 17:29:55 -07:00
Alexander Rose
f8ce9cbb65 determine type of sequence for alignment 2020-05-06 15:38:51 -07:00
Alexander Rose
2af9d1cabf limit by chains superposition to polymers 2020-05-06 11:32:31 -07:00
Alexander Rose
e8d1737d40 backbone/sidechain query fixes
- handle non-polymer components in polymers
2020-05-06 10:56:01 -07:00
Alexander Rose
0328e93518 interactivity: selectOnly, only deselect for the structure of the given loci 2020-05-06 10:37:11 -07:00
Alexander Rose
8a4ab9bdb9 more selection helper fixes
- use structure from last decorator as reference
- handle that oldObj is not defined for inserts
2020-05-05 22:10:53 -07:00
Alexander Rose
410cdb193d selection manager fixes
- add removed/updated events to substructure-parent-helper
- remap selections
2020-05-05 17:03:04 -07:00
Alexander Rose
a278337b4c Merge branch 'master' of https://github.com/molstar/molstar 2020-05-05 11:08:46 -07:00
Alexander Rose
b1308de0b9 StructureSelectionManager improvements
- remap/clear referenceLoci onUpdate
- remap/clear history onUpdate
- removed unused prevHighlight
- support group-by-structure in modifyHistory
2020-05-05 11:08:05 -07:00
Alexander Rose
9705078970 set writeDepth specifically for points and text geo 2020-05-05 10:12:51 -07:00
Alexander Rose
b1ca98e945 ignore atoms with zero occupancy for bond computation
- assuming they are not actually atoms
2020-05-05 09:54:47 -07:00
David Sehnal
35054eaca9 0.7.0-dev.19 2020-05-05 17:45:21 +02:00
David Sehnal
2747c743c9 fix ElementLocationIterator.advance
- extra element was added if last unit size === 1
2020-05-05 17:41:17 +02:00
Alexander Rose
031d08a8d4 Merge branch 'master' of https://github.com/molstar/molstar 2020-05-04 19:31:09 -07:00
Alexander Rose
7cc6c4a9c8 superposition fixes 2020-05-04 19:30:57 -07:00
David Sehnal
ff27098514 refactored PluginSpec.config 2020-05-05 00:32:43 +02:00
David Sehnal
545cd65066 Merge branch 'master' of https://github.com/molstar/molstar 2020-05-04 22:32:53 +02:00
David Sehnal
84bfc6e7a9 VolumeStreamingControls show "update" button with "reset params" button 2020-05-04 22:31:55 +02:00
David Sehnal
2f71c4c5e4 VolumeStreamingCustomControls bounded ranges for fine grained control 2020-05-04 22:09:28 +02:00
Alexander Rose
1448f7aeb6 added PluginConfig.Structure.SizeThresholds 2020-05-04 13:00:45 -07:00
David Sehnal
79d66a5cfc revert select binding change 2020-05-04 19:42:33 +02:00
Alexander Rose
2ec19ac04c fixed rsrz label 2020-05-04 10:08:48 -07:00
David Sehnal
f62a6d4512 Selection mode: change binding to right-click
- this way the non-selection mode behavior stays the same and the right click adds functionality
2020-05-04 17:48:37 +02:00
David Sehnal
4fbcee3953 Basic Selection mode UI help
- could use some improvement
2020-05-04 17:42:31 +02:00
David Sehnal
12bb283b97 StructureSelectionManager.clear also clears selection history 2020-05-04 17:19:48 +02:00
David Sehnal
13d776c7cb Icon tweaks
- measurement remove
- selection mode
2020-05-04 17:14:40 +02:00
David Sehnal
f45b48c6e1 slider CSS fix 2020-05-04 17:00:02 +02:00
David Sehnal
ff14c94a90 PluginContext.isBusy fix + related UI fix 2020-05-04 12:08:57 +02:00
David Sehnal
0a0ef35b74 CombinedColorControl: RGB input as separate boxes 2020-05-04 11:48:45 +02:00
David Sehnal
e3dc10c085 ObjectListEditor: bugfix 2020-05-04 11:31:30 +02:00
David Sehnal
46113bf3d4 PluginStateAnimation.canApply 2020-05-03 20:43:49 +02:00
David Sehnal
0f3ef61f7d isBusy behavior bugfix
- was causing the animation button not to display if the state was loaded too fast
2020-05-03 13:10:52 +02:00
Alexander Rose
86aae08257 assembly-symmetry: dihedral cage fixes 2020-05-02 12:59:54 -07:00
Alexander Rose
06bf2c39a1 add pdb-dev download bcif encoding 2020-05-02 12:59:31 -07:00
Alexander Rose
66a23bc2a2 alignment fixes 2020-05-02 12:58:32 -07:00
David Sehnal
51e86f1e43 Superposition UI: add "toggle selection" button to help 2020-05-02 13:40:27 +02:00
Alexander Rose
78c70b3f5b structure superposition by chains or by atoms
- superposition by chains can be guided by sequence alignment
- TODO not working with already transformed structures in the general case
2020-05-01 19:08:31 -07:00
Alexander Rose
8f52ffe061 move state/session io warning to snapshot ui 2020-05-01 13:06:25 -07:00
Alexander Rose
e95b91ab84 typo 2020-04-30 15:51:54 -07:00
Alexander Rose
f4dbd66496 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-30 15:49:31 -07:00
Alexander Rose
5895df0499 tweaked elementInstances granularity label 2020-04-30 15:49:16 -07:00
Alexander Rose
6d0d88f3be added sidechain queries 2020-04-30 15:44:41 -07:00
David Sehnal
7e71428cc3 RemoteStateSnapshots: fix unmounted setState 2020-05-01 00:15:22 +02:00
David Sehnal
2e215440f7 Merge branch 'master' of https://github.com/molstar/molstar 2020-05-01 00:09:29 +02:00
David Sehnal
c04fa56c6c fixed expandConnected query, added expand to cov/metallic bonds 2020-05-01 00:09:13 +02:00
Alexander Rose
6c70b5e38f take structure volume into account in getQualityProps for resolution 2020-04-30 15:00:40 -07:00
Alexander Rose
ad9160a4a3 guard against overly high surface resolution 2020-04-30 15:00:07 -07:00
Alexander Rose
c747d3928e always apply marker in shader 2020-04-30 11:34:50 -07:00
David Sehnal
68e8d67054 mol-plugin: PluginState.dispose unregisters behaviors 2020-04-30 18:29:47 +02:00
David Sehnal
9e8fc76d28 StructureSelectionManager: loci events 2020-04-30 16:02:16 +02:00
Alexander Rose
d8970305de cellpack: cleanup
- renamed GetAllAssamblyinOne to StructureFromAssemblies
- removed GetAllAssamblyinOneStructure
2020-04-29 22:21:22 -07:00
Alexander Rose
824675a658 cellpack: split color themes 2020-04-29 21:49:37 -07:00
Alexander Rose
090ad613cc Merge pull request #35 from corredD/forkdev
Forkdev
2020-04-29 20:49:00 -07:00
Alexander Rose
ea35e09c96 focus repr carbon color tweaks 2020-04-29 14:19:45 -07:00
Alexander Rose
d8b9a1a560 basic nw sequence alignment 2020-04-29 13:54:00 -07:00
Alexander Rose
c259f58e63 make carbon color configurable in element symbol color theme 2020-04-29 13:33:31 -07:00
Alexander Rose
9d4c2a1147 support setting pdb/emdb provider in viewer index.html 2020-04-29 13:32:58 -07:00
David Sehnal
f13c3fe38b Viewport resize handling fix 2020-04-29 15:14:35 +02:00
David Sehnal
60409df145 better Viewport resize handling 2020-04-29 15:13:53 +02:00
autin
1d7321cd6f Merge branch 'master' of https://github.com/molstar/molstar into forkdev
# Conflicts:
#	src/extensions/cellpack/property.ts
2020-04-29 10:01:39 +02:00
Alexander Rose
eb68ccbf6b very basic handle-helper 2020-04-28 23:59:54 -07:00
Alexander Rose
b1ece44c49 0.7.0-dev.18 2020-04-28 17:06:40 -07:00
Alexander Rose
37ae274fb6 force bond compute for sequence positions with micro-heterogeneity
- see e.g. 3NIR
2020-04-28 17:04:13 -07:00
Alexander Rose
c900045fcd refactored sequence to handle partial structures 2020-04-28 16:34:12 -07:00
Alexander Rose
50d95ccf6a volume refactoring
- renamed VolumeData to Grid
- renamed VolumeData.data to cells
- renamed VolumeData.dataStats to stats
- added grid to Volume
- added label to Volume
- added custom props to volume
- use Volume instead of VolumeData/Grid as main object
2020-04-28 12:19:42 -07:00
Alexander Rose
c9171444eb added State.root and fix StateAction.createDefaultParams/.params 2020-04-28 10:06:22 -07:00
autin
56ea62af71 Merge branch 'master' of https://github.com/molstar/molstar into forkdev 2020-04-28 11:06:27 +02:00
Alexander Rose
9e81626928 use extensions in embedded viewer to not break compat 2020-04-27 23:35:21 -07:00
Alexander Rose
84fda6e35d 0.7.0-dev.17 2020-04-27 20:01:02 -07:00
Alexander Rose
0f758cf554 fix webpack prod build setup 2020-04-27 19:59:25 -07:00
Alexander Rose
a6605052db 0.7.0-dev.16 2020-04-27 19:54:37 -07:00
Alexander Rose
8514175da2 add build/viewer/ to published files 2020-04-27 19:53:01 -07:00
Alexander Rose
6a49427fc0 0.7.0-dev.15 2020-04-27 19:44:01 -07:00
Alexander Rose
7c18e5eb86 refactored viewer app to make it usable for simple embedded use-cases 2020-04-27 19:41:43 -07:00
Alexander Rose
2a7d258715 add Download providers to Config 2020-04-27 19:32:42 -07:00
Alexander Rose
54fb9beeee add State.Snapshots.OpenUrl 2020-04-27 19:31:55 -07:00
Alexander Rose
27ebbc50d5 allow animation button to be hidden 2020-04-27 19:31:07 -07:00
Alexander Rose
2a1b6e52b2 be clear that StateAction.params need to work without data object 2020-04-27 19:29:55 -07:00
David Sehnal
3110e82d92 0.7.0-dev.14 2020-04-28 01:03:33 +02:00
David Sehnal
4be999ce32 StructureMeasurementManagerState add options 2020-04-28 01:01:17 +02:00
David Sehnal
f0d7a4ed2a custom label for distance/angle/dihedral 2020-04-27 18:15:43 +02:00
David Sehnal
2dacfcb485 Dihedrals: fix arcs and labels 2020-04-27 18:08:22 +02:00
David Sehnal
6218cc5371 PluginComponent.subscribe returns the subscription 2020-04-27 17:24:58 +02:00
autin
b4808f2909 resampling optional. added the latest models 2020-04-27 12:32:20 +02:00
autin
1a2e9eaa84 Merge branch 'master' of https://github.com/molstar/molstar into forkdev 2020-04-27 09:24:00 +02:00
David Sehnal
056ce42097 mol-plugin: canvas3d.initialized event/behavior fix 2020-04-27 03:25:58 +02:00
David Sehnal
b14b5ca626 0.7.0-dev.13 2020-04-27 02:05:52 +02:00
David Sehnal
ffbaa944f2 tweak publish scripts 2020-04-27 02:03:47 +02:00
David Sehnal
e2ba96174a Merge branch 'master' of https://github.com/molstar/molstar 2020-04-27 02:02:31 +02:00
David Sehnal
8c5d99bb54 higher quality logo, add link, move to corner 2020-04-27 02:02:21 +02:00
Alexander Rose
b18b3be070 basic mol2 format support 2020-04-26 14:08:19 -07:00
Alexander Rose
2e69b7c419 CubeProvider tweaks, check if orbitals 2020-04-26 13:06:35 -07:00
Alexander Rose
5007f5fb72 added VolumeData.sourceData: ModelFormat 2020-04-26 13:05:39 -07:00
Alexander Rose
6fe83a9a70 cellpack: fixed pdb fallback when opm fails 2020-04-26 12:27:30 -07:00
Alexander Rose
20af084127 increate outline threshold max value 2020-04-26 11:49:54 -07:00
Alexander Rose
d6501170e6 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-26 11:34:29 -07:00
Alexander Rose
5f33364514 ignore pickable=false renderObjects completely 2020-04-26 11:34:07 -07:00
David Sehnal
7924c008fa proteopedia-wrapper: return result of snapshot.fetch 2020-04-26 20:32:12 +02:00
David Sehnal
2d2a53f28e 0.7.0-dev.12 2020-04-26 19:00:27 +02:00
David Sehnal
1f7ffabef9 added PhysicalSizeTheme.scale 2020-04-26 18:59:17 +02:00
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
autin
7e3cca5780 Merge branch 'master' of https://github.com/molstar/molstar into forkdev
# Conflicts:
#	src/extensions/cellpack/model.ts
2020-04-26 11:50:33 +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
autin
0b9371527e Merge branch 'master' of https://github.com/molstar/molstar into forkdev 2020-04-25 08:30:19 +02: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
autin
7aee2d805d eslint fix 2020-04-24 15:01:21 +02:00
autin
06f03f399a lipid membrane tiles as assembly support, added ingredient colors if provided. 2020-04-24 14:57:14 +02:00
197 changed files with 3508 additions and 1465 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.7.0-dev.7",
"version": "0.7.1-dev.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.7.0-dev.7",
"version": "0.7.1-dev.2",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -35,11 +35,12 @@
"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"
"version": "npm run build",
"postversion": "git push && git push --tags"
},
"files": [
"lib/"
"lib/",
"build/viewer/"
],
"bin": {
"cif2bcif": "lib/apps/cif2bcif/index.js",

View File

@@ -117,7 +117,7 @@ export function printSequence(model: Model) {
for (const key of Object.keys(byEntityKey)) {
const { sequence, entityId } = byEntityKey[+key];
const { seqId, compId } = sequence;
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)}, ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
console.log(`${Sequence.getSequenceString(sequence)}`);
}
console.log();

View File

@@ -8,38 +8,36 @@ import * as fs from 'fs';
import * as argparse from 'argparse';
import * as util from 'util';
import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
import { Volume } 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';
import { Task } from '../../mol-task';
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
import { Theme } from '../../mol-theme/theme';
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
import { volumeFromDensityServerData, DscifFormat } from '../../mol-model-formats/volume/density-server';
require('util.promisify').shim();
const writeFileAsync = util.promisify(fs.writeFile);
type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
async function getVolume(url: string): Promise<Volume> {
const cif = await downloadCif(url, true);
const data = CIF.schema.densityServer(cif.blocks[1]);
return { source: data, volume: await volumeFromDensityServerData(data).run() };
return await volumeFromDensityServerData(data).run();
}
function print(data: Volume) {
const { volume_data_3d_info } = data.source;
function print(volume: Volume) {
if (!DscifFormat.is(volume.sourceData)) return;
const { volume_data_3d_info } = volume.sourceData.data;
const row = Table.getRow(volume_data_3d_info, 0);
console.log(row);
if (data.volume.transform) console.log(data.volume.transform);
console.log(data.volume.dataStats);
console.log(volume.grid.transform);
console.log(volume.grid.stats);
}
async function doMesh(data: Volume, filename: string) {
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
async function doMesh(volume: Volume, filename: string) {
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
// Export the mesh in OBJ format.

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<title>Embedded Mol* Viewer</title>
<style>
#app {
position: absolute;
left: 100px;
top: 100px;
width: 800px;
height: 600px;
}
</style>
<link rel="stylesheet" type="text/css" href="molstar.css" />
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="./molstar.js"></script>
<script type="text/javascript">
var viewer = new molstar.Viewer('app', {
layoutIsExpanded: false,
layoutShowControls: false,
layoutShowRemoteState: false,
layoutShowSequence: true,
layoutShowLog: false,
layoutShowLeftPanel: true,
viewportShowExpand: true,
viewportShowSelectionMode: false,
viewportShowAnimation: false,
pdbProvider: 'rcsb',
emdbProvider: 'rcsb',
});
viewer.loadPdb('7bv2');
viewer.loadEmdb('EMD-30210');
// TODO add Volume.customProperty and load suggested isoValue via custom property
var sub = viewer.plugin.managers.volume.hierarchy.behaviors.selection.subscribe(function (value) {
if (value.volume?.representations[0]) {
var ref = value.volume.representations[0].cell;
var tree = viewer.plugin.state.data.build().to(ref).update({
type: {
name: 'isosurface',
params: {
isoValue: {
kind: 'relative',
relativeValue: 6
}
}
},
colorTheme: ref.transform.params?.colorTheme
});
viewer.plugin.runTask(viewer.plugin.state.data.updateTree(tree));
if (typeof sub !== 'undefined') sub.unsubscribe();
}
});
</script>
</body>
</html>

View File

@@ -34,10 +34,50 @@
height: 600px;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<link rel="stylesheet" type="text/css" href="molstar.css" />
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="./index.js"></script>
<script type="text/javascript" src="./molstar.js"></script>
<script type="text/javascript">
function getParam(name, regex) {
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
}
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
if (debugMode) molstar.setDebugMode(debugMode);
var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
var viewer = new molstar.Viewer('app', {
layoutShowControls: !hideControls,
viewportShowExpand: false,
pdbProvider: pdbProvider || 'pdbe',
emdbProvider: emdbProvider || 'pdbe',
});
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
var snapshotUrl = getParam('snapshot-url', '[^&]+').trim();
var snapshotUrlType = getParam('snapshot-url-type', '[^&]+').toLowerCase().trim() || 'molj';
if (snapshotUrl && snapshotUrlType) viewer.loadSnapshotFromUrl(snapshotUrl, snapshotUrlType);
var structureUrl = getParam('structure-url', '[^&]+').trim();
var structureUrlFormat = getParam('structure-url-format', '[a-z]+').toLowerCase().trim();
var structureUrlIsBinary = getParam('structure-url-is-binary', '[^&]+').trim() === '1';
if (structureUrl) viewer.loadStructureFromUrl(structureUrl, structureUrlFormat, structureUrlIsBinary);
var pdb = getParam('pdb', '[^&]+').trim();
if (pdb) viewer.loadPdb(pdb);
var pdbDev = getParam('pdb-dev', '[^&]+').trim();
if (pdbDev) viewer.loadPdbDev(pdbDev);
var emdb = getParam('emdb', '[^&]+').trim();
if (emdb) viewer.loadEmdb(emdb);
</script>
</body>
</html>

View File

@@ -8,84 +8,114 @@
import '../../mol-util/polyfill';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html';
import './embedded.html';
import './favicon.ico';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginSpec } from '../../mol-plugin/spec';
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { PluginConfig } from '../../mol-plugin/config';
import { CellPack } from '../../extensions/cellpack';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { Asset } from '../../mol-util/assets';
import { ObjectKeys } from '../../mol-util/type-helpers';
import { PluginState } from '../../mol-plugin/state';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
require('mol-plugin-ui/skin/light.scss');
function getParam(name: string, regex: string): string {
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
}
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
const hideControls = getParam('hide-controls', `[^&]+`) === '1';
const Extensions = {
'cellpack': PluginSpec.Behavior(CellPack),
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport)
};
function init() {
const spec: PluginSpec = {
actions: [...DefaultPluginSpec.actions],
behaviors: [
...DefaultPluginSpec.behaviors,
PluginSpec.Behavior(CellPack),
PluginSpec.Behavior(PDBeStructureQualityReport),
PluginSpec.Behavior(RCSBAssemblySymmetry),
PluginSpec.Behavior(RCSBValidationReport),
],
animations: [...DefaultPluginSpec.animations || []],
customParamEditors: DefaultPluginSpec.customParamEditors,
layout: {
initial: {
isExpanded: true,
showControls: !hideControls
const DefaultViewerOptions = {
extensions: ObjectKeys(Extensions),
layoutIsExpanded: true,
layoutShowControls: true,
layoutShowRemoteState: true,
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
layoutShowSequence: true,
layoutShowLog: true,
layoutShowLeftPanel: true,
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
};
type ViewerOptions = typeof DefaultViewerOptions;
export class Viewer {
plugin: PluginContext
constructor(elementId: string, options: Partial<ViewerOptions> = {}) {
const o = { ...DefaultViewerOptions, ...options };
const spec: PluginSpec = {
actions: [...DefaultPluginSpec.actions],
behaviors: [
...DefaultPluginSpec.behaviors,
...o.extensions.map(e => Extensions[e]),
],
animations: [...DefaultPluginSpec.animations || []],
customParamEditors: DefaultPluginSpec.customParamEditors,
layout: {
initial: {
isExpanded: o.layoutIsExpanded,
showControls: o.layoutShowControls,
controlsDisplay: o.layoutControlsDisplay,
},
controls: {
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls,
top: o.layoutShowSequence ? undefined : 'none',
bottom: o.layoutShowLog ? undefined : 'none',
left: o.layoutShowLeftPanel ? undefined : 'none',
}
},
controls: {
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
}
},
config: DefaultPluginSpec.config
};
spec.config?.set(PluginConfig.Viewport.ShowExpand, false);
const plugin = createPlugin(document.getElementById('app')!, spec);
trySetSnapshot(plugin);
tryLoadFromUrl(plugin);
}
components: {
...DefaultPluginSpec.components,
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
},
config: [
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
[PluginConfig.State.DefaultServer, o.pluginStateServer],
[PluginConfig.State.CurrentServer, o.pluginStateServer],
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
]
};
async function trySetSnapshot(ctx: PluginContext) {
try {
const snapshotUrl = getParam('snapshot-url', `[^&]+`);
const snapshotId = getParam('snapshot-id', `[^&]+`);
if (!snapshotUrl && !snapshotId) return;
// TODO parametrize the server
const url = snapshotId
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
: snapshotUrl;
await PluginCommands.State.Snapshots.Fetch(ctx, { url });
} catch (e) {
ctx.log.error('Failed to load snapshot.');
console.warn('Failed to load snapshot', e);
const element = document.getElementById(elementId);
if (!element) throw new Error(`Could not get element with id '${elementId}'`);
this.plugin = createPlugin(element, spec);
}
}
async function tryLoadFromUrl(ctx: PluginContext) {
const url = getParam('loadFromURL', '[^&]+').trim();
try {
if (!url) return;
async setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
await PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
let format = 'cif', isBinary = false;
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
case 'pdb': format = 'pdb'; break;
case 'mmbcif': isBinary = true; break;
}
async loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
await PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
async loadStructureFromUrl(url: string, format = 'cif', isBinary = false) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
@@ -96,10 +126,57 @@ async function tryLoadFromUrl(ctx: PluginContext) {
}
}
}));
} catch (e) {
ctx.log.error(`Failed to load from URL (${url})`);
console.warn(`Failed to load from URL (${url})`, e);
}
}
init();
async loadPdb(pdb: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb' as const,
params: {
provider: {
id: pdb,
server: {
name: provider,
params: PdbDownloadProvider[provider].defaultValue as any
}
},
options: params.source.params.options,
}
}
}));
}
async loadPdbDev(pdbDev: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'pdb-dev' as const,
params: {
provider: {
id: pdbDev,
encoding: 'bcif',
},
options: params.source.params.options,
}
}
}));
}
async loadEmdb(emdb: string) {
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
source: {
name: 'pdb-emd-ds' as const,
params: {
provider: {
id: emdb,
server: provider,
},
detail: 3,
}
}
}));
}
}

View File

@@ -41,7 +41,7 @@
display: block;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<link rel="stylesheet" type="text/css" href="molstar.css" />
<script type="text/javascript" src="./index.js"></script>
</head>
<body>
@@ -55,13 +55,13 @@
</select>
</div>
<div id="app"></div>
<script>
<script>
function $(id) { return document.getElementById(id); }
var pdbId = '1grm', assemblyId= '1';
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
var format = 'mmcif';
$('url').value = url;
$('url').onchange = function (e) { url = e.target.value; }
$('assemblyId').value = assemblyId;
@@ -86,7 +86,7 @@
addHeader('Camera');
addControl('Toggle Spin', () => BasicMolStarWrapper.toggleSpin());
addSeparator();
addHeader('Animation');
@@ -115,7 +115,7 @@
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());

View File

@@ -6,7 +6,7 @@
import { Mat4 } from '../../mol-math/linear-algebra';
import { QueryContext, StructureSelection } from '../../mol-model/structure';
import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
import { superpose } from '../../mol-model/structure/structure/util/superposition';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
@@ -79,7 +79,7 @@ export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], c
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);
const transforms = superpose(selections);
await siteVisual(plugin, xs[0].cell, pivot, rest);
for (let i = 1; i < selections.length; i++) {

View File

@@ -38,16 +38,16 @@
display: block;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<link rel="stylesheet" type="text/css" href="molstar.css" />
<script type="text/javascript" src="./index.js"></script>
</head>
<body>
<div id='controls'></div>
<div id="app"></div>
<script>
<script>
LightingDemo.init('app')
LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
addHeader('Example PDB IDs');
addControl('1M07', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' }));
addControl('6HY0', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6HY0.cif', assemblyId: '1' }));

View File

@@ -48,7 +48,7 @@
width: 300px;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<link rel="stylesheet" type="text/css" href="molstar.css" />
<script type="text/javascript" src="./index.js"></script>
</head>
<body>
@@ -65,7 +65,7 @@
<div id="app"></div>
<div id="volume-streaming-wrapper"></div>
<script>
// it might be a good idea to define these colors in a separate script file
// it might be a good idea to define these colors in a separate script file
var CustomColors = [0x00ff00, 0x0000ff];
// create an instance of the plugin
@@ -74,11 +74,11 @@
console.log('Wrapper version', MolStarProteopediaWrapper.VERSION_MAJOR, MolStarProteopediaWrapper.VERSION_MINOR);
function $(id) { return document.getElementById(id); }
var pdbId = '1cbs', assemblyId= 'preferred', isBinary = true;
var url = 'https://www.ebi.ac.uk/pdbe/entry-files/download/' + pdbId + '.bcif'
var format = 'cif';
$('url').value = url;
$('url').onchange = function (e) { url = e.target.value; }
$('assemblyId').value = assemblyId;
@@ -92,6 +92,12 @@
// 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 { }
hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
@@ -103,7 +109,7 @@
customColorList: CustomColors
});
PluginWrapper.setBackground(0xffffff);
PluginWrapper.load({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.toggleSpin();
PluginWrapper.events.modelInfo.subscribe(function (info) {
@@ -111,8 +117,8 @@
listHetGroups(info);
});
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format, isBinary }));
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, isBinary, assemblyId: assemblyId }));
addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
addSeparator();
@@ -138,7 +144,7 @@
// Same as "wheel icon" and Viewport options
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
addSeparator();
addHeader('Animation');
@@ -171,7 +177,7 @@
addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
addControl('Remove', () => PluginWrapper.experimentalData.remove());
addSeparator();
addSeparator();
addHeader('State');
var snapshot;
@@ -185,10 +191,10 @@
PluginWrapper.snapshot.set(snapshot);
});
addControl('Download State', () => {
snapshot = PluginWrapper.snapshot.download('molj');
PluginWrapper.snapshot.download('molj');
});
addControl('Download Session', () => {
snapshot = PluginWrapper.snapshot.download('molx');
PluginWrapper.snapshot.download('molx');
});
////////////////////////////////////////////////////////

View File

@@ -5,35 +5,34 @@
*/
import * as ReactDOM from 'react-dom';
import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { EvolutionaryConservation } from './annotation';
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { volumeStreamingControls } from './ui/controls';
import { PluginState } from '../../mol-plugin/state';
import { Scheduler } from '../../mol-task';
import { createProteopediaCustomTheme } from './coloring';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { ColorNames } from '../../mol-util/color/names';
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
import { download } from '../../mol-util/download';
import { getFormattedTime } from '../../mol-util/date';
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { PluginState } from '../../mol-plugin/state';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { EvolutionaryConservation } from './annotation';
import { createProteopediaCustomTheme } from './coloring';
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
import './index.html';
import { volumeStreamingControls } from './ui/controls';
require('../../mol-plugin-ui/skin/light.scss');
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 5;
static VERSION_MINOR = 4;
static VERSION_MINOR = 5;
private _ev = RxEventHelper.create();
@@ -233,7 +232,6 @@ class MolStarProteopediaWrapper {
await this.updateStyle(representationStyle);
this.loadedParams = { url, format, assemblyId };
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
}
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
@@ -251,9 +249,7 @@ class MolStarProteopediaWrapper {
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
}
viewport = {
@@ -407,7 +403,7 @@ class MolStarProteopediaWrapper {
try {
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}`));
return await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
} catch (e) {
console.log(e);
}

View File

@@ -4,29 +4,28 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../mol-theme/theme';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Color } from '../../mol-util/color';
import { getPalette } from '../../mol-util/color/palette';
import { ColorTheme, LocationColor } from '../../mol-theme/color';
import { ScaleLegend, TableLegend } from '../../mol-util/legend';
import { StructureElement, Bond } from '../../mol-model/structure';
import { Location } from '../../mol-model/location';
import { CellPackInfoProvider } from './property';
import { distinctColors } from '../../mol-util/color/distinct';
import { Hcl } from '../../mol-util/color/spaces/hcl';
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Color } from '../../../mol-util/color';
import { getPalette } from '../../../mol-util/color/palette';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
import { StructureElement, Bond } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { CellPackInfoProvider } from '../property';
import { distinctColors } from '../../../mol-util/color/distinct';
import { Hcl } from '../../../mol-util/color/spaces/hcl';
const DefaultColor = Color(0xCCCCCC);
const Description = 'Gives every model in a CellPack packing a unique color similar to other models in the packing.';
const Description = 'Gives every model in a CellPack packing a unique generated 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 const CellPackGenerateColorThemeParams = {};
export type CellPackGenerateColorThemeParams = typeof CellPackGenerateColorThemeParams
export function getCellPackGenerateColorThemeParams(ctx: ThemeDataContext) {
return CellPackGenerateColorThemeParams; // TODO return copy
}
export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackColorThemeParams>): ColorTheme<CellPackColorThemeParams> {
export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackGenerateColorThemeParams>): ColorTheme<CellPackGenerateColorThemeParams> {
let color: LocationColor;
let legend: ScaleLegend | TableLegend | undefined;
@@ -34,7 +33,8 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
if (ctx.structure && info) {
const colors = distinctColors(info.packingsCount);
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
const { models } = ctx.structure.root;
@@ -70,7 +70,7 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
}
return {
factory: CellPackColorTheme,
factory: CellPackGenerateColorTheme,
granularity: 'instance',
color,
props,
@@ -79,13 +79,13 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
};
}
export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeParams, 'cellpack'> = {
name: 'cellpack',
label: 'CellPack',
export const CellPackGenerateColorThemeProvider: ColorTheme.Provider<CellPackGenerateColorThemeParams, 'cellpack-generate'> = {
name: 'cellpack-generate',
label: 'CellPack Generate',
category: ColorTheme.Category.Chain,
factory: CellPackColorTheme,
getParams: getCellPackColorThemeParams,
defaultValues: PD.getDefaultValues(CellPackColorThemeParams),
factory: CellPackGenerateColorTheme,
getParams: getCellPackGenerateColorThemeParams,
defaultValues: PD.getDefaultValues(CellPackGenerateColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => {
return (
!!ctx.structure && ctx.structure.elementCount > 0 &&
@@ -93,4 +93,5 @@ export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeP
!!CellPackInfoProvider.get(ctx.structure).value
);
}
};
};

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ThemeDataContext } from '../../../mol-theme/theme';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Color } from '../../../mol-util/color';
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
import { StructureElement } from '../../../mol-model/structure';
import { Location } from '../../../mol-model/location';
import { CellPackInfoProvider } from '../property';
const DefaultColor = Color(0xCCCCCC);
const Description = 'Gives every model in a CellPack the color provied in the packing data.';
export const CellPackProvidedColorThemeParams = {};
export type CellPackProvidedColorThemeParams = typeof CellPackProvidedColorThemeParams
export function getCellPackProvidedColorThemeParams(ctx: ThemeDataContext) {
return CellPackProvidedColorThemeParams; // TODO return copy
}
export function CellPackProvidedColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackProvidedColorThemeParams>): ColorTheme<CellPackProvidedColorThemeParams> {
let color: LocationColor;
let legend: ScaleLegend | TableLegend | undefined;
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
if (ctx.structure && info?.colors) {
const { models } = ctx.structure.root;
const modelColor = new Map<number, Color>();
for (let i = 0, il = models.length; i < il; ++i) {
const idx = models[i].trajectoryInfo.index;
modelColor.set(models[i].trajectoryInfo.index, info.colors[idx]);
}
color = (location: Location): Color => {
return StructureElement.Location.is(location)
? modelColor.get(location.unit.model.trajectoryInfo.index)!
: DefaultColor;
};
} else {
color = () => DefaultColor;
}
return {
factory: CellPackProvidedColorTheme,
granularity: 'instance',
color,
props,
description: Description,
legend
};
}
export const CellPackProvidedColorThemeProvider: ColorTheme.Provider<CellPackProvidedColorThemeParams, 'cellpack-provided'> = {
name: 'cellpack-provided',
label: 'CellPack Provided',
category: ColorTheme.Category.Chain,
factory: CellPackProvidedColorTheme,
getParams: getCellPackProvidedColorThemeParams,
defaultValues: PD.getDefaultValues(CellPackProvidedColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => {
return (
!!ctx.structure && ctx.structure.elementCount > 0 &&
ctx.structure.models[0].trajectoryInfo.size > 1 &&
!!CellPackInfoProvider.get(ctx.structure).value?.colors
);
}
};

View File

@@ -191,8 +191,14 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
const rpTmpVec1 = Vec3();
export function getMatFromResamplePoints(points: NumberArray, segmentLength: number) {
const new_points = ResampleControlPoints(points, segmentLength);
export function getMatFromResamplePoints(points: NumberArray, segmentLength: number, resample: boolean) {
let new_points: Vec3[] = [];
if (resample) new_points = ResampleControlPoints(points, segmentLength);
else {
for (let idx = 0; idx < points.length / 3; ++idx){
new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
}
}
const npoints = new_points.length;
const new_normal = GetSmoothNormals(new_points);
const frames = GetMiniFrame(new_points, new_normal);

View File

@@ -59,6 +59,7 @@ export interface Ingredient {
radii?: [Radii];
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
nbCurve?: number;
uLength?: number;
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
[curveX: string]: unknown;
/** the orientation in the membrane */
@@ -66,6 +67,8 @@ export interface Ingredient {
/** offset along membrane */
offset?: Vec3;
ingtype?: string;
color?: Vec3;
confidence?: number;
}
export interface IngredientSource {

View File

@@ -5,9 +5,9 @@
*/
import { PluginBehavior } from '../../mol-plugin/behavior';
import { CellPackColorThemeProvider } from './color';
import { LoadCellPackModel } from './model';
import { CellPackGenerateColorThemeProvider } from './color/generate';
import { CellPackProvidedColorThemeProvider } from './color/provided';
export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'cellpack',
@@ -19,12 +19,14 @@ export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip
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);
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackGenerateColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackProvidedColorThemeProvider);
}
unregister() {
this.ctx.state.data.actions.remove(LoadCellPackModel);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackGenerateColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackProvidedColorThemeProvider);
}
}
});

View File

@@ -17,7 +17,7 @@ import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../mol-math/geometry';
import { Task, RuntimeContext } from '../../mol-task';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies } from './state';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../mol-script/runtime/query/compiler';
@@ -27,61 +27,76 @@ import { Column } from '../../mol-data/db';
import { createModels } from '../../mol-model-formats/structure/basic/parser';
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { readFromFile } from '../../mol-util/data-source';
import { objectForEach } from '../../mol-util/object';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
}
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, file?: Asset.File) {
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 model_id = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
let model: Model;
let trajectory = trajCache.get(id);
let assets: Asset.Wrapper[] = [];
if (file) {
if (file.name.endsWith('.cif')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const cif = (await parseCif(plugin, text.data)).blocks[0];
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
} else if (file.name.endsWith('.bcif')) {
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
assets.push(binary);
const cif = (await parseCif(plugin, binary.data)).blocks[0];
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
} else if (file.name.endsWith('.pdb')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const pdb = await parsePDBfile(plugin, text.data, id);
model = (await plugin.runTask(trajectoryFromPDB(pdb)))[model_id];
} else {
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
const data = await getFromOPM(plugin, id, assetManager);
if (data.asset){
assets.push(data.asset);
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
if (!trajectory) {
if (file) {
if (file.name.endsWith('.cif')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const cif = (await parseCif(plugin, text.data)).blocks[0];
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
} else if (file.name.endsWith('.bcif')) {
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
assets.push(binary);
const cif = (await parseCif(plugin, binary.data)).blocks[0];
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
} else if (file.name.endsWith('.pdb')) {
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
assets.push(text);
const pdb = await parsePDBfile(plugin, text.data, id);
trajectory = await plugin.runTask(trajectoryFromPDB(pdb));
} else {
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
try {
const data = await getFromOPM(plugin, id, assetManager);
assets.push(data.asset);
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
} catch (e) {
// fallback to getFromPdb
// console.error(e);
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
}
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
}
} else {
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
assets.push(asset);
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
}
} else {
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
assets.push(data.asset);
if ('pdb' in data) {
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
} else {
model = (await plugin.runTask(trajectoryFromMmCIF(data.mmcif)))[model_id];
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 };
}
@@ -94,7 +109,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
}
let query;
if (source.selection){
const asymIds: string[] = source.selection.replace(' :', '').split(' or');
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
@@ -133,7 +148,6 @@ function getTransform(trans: Vec3, rot: Quat) {
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]));
@@ -142,11 +156,15 @@ function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0;
const instances: Mat4[] = [];
const segmentLength = ingredient.radii
? (ingredient.radii[0].radii
let segmentLength = 3.4;
if (ingredient.uLength){
segmentLength = ingredient.uLength;
} else if (ingredient.radii){
segmentLength = ingredient.radii[0].radii
? ingredient.radii[0].radii[0] * 2.0
: 3.4)
: 3.4;
: 3.4;
}
let resampling: boolean = false;
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`;
if (!(cname in ingredient)) {
@@ -158,12 +176,17 @@ function getCurveTransforms(ingredient: Ingredient) {
// TODO handle curve with 2 or less points
continue;
}
// test for resampling
let distance: number = Vec3.distance(_points[0], _points[1]);
if (distance >= segmentLength + 2.0) {
console.info(distance);
resampling = true;
}
const points = new Float32Array(_points.length * 3);
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3);
const newInstances = getMatFromResamplePoints(points, segmentLength);
const newInstances = getMatFromResamplePoints(points, segmentLength, resampling);
instances.push(...newInstances);
}
return instances;
}
@@ -277,7 +300,6 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
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);
@@ -288,22 +310,22 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
return getStructure(plugin, curveModel, ingredient.source);
}
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
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 === 'HIV1_CAhex_0_1_0') return; // 1VU4CtoH_hex.pdb
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return; // 1AK4fitTo1VU4hex.pdb
if (name === 'iLDL') return; // EMD-5239
if (name === 'peptides') return; // peptide.pdb
if (name === 'lypoglycane') return;
}
// model id in case structure is NMR
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, file);
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
if (!model) return;
let structure: Structure;
@@ -354,13 +376,22 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing;
const assets: Asset.Wrapper[] = [];
const trajCache = new TrajectoryCache();
const structures: Structure[] = [];
const colors: Color[] = [];
let skipColors: boolean = false;
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
if (ingredientStructure) {
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
const c = ingredients[iName].color;
if (c){
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
} else {
skipColors = true;
}
}
}
@@ -385,7 +416,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
trajectoryInfo.size = il;
trajectoryInfo.index = i;
}
return { structure, assets };
return { structure, assets, colors: skipColors ? undefined : colors };
});
}
@@ -421,11 +452,25 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
break;
}
}
if (!file){
// check for cif directly
const cifileName = `${name}.cif`;
for (const f of params.ingredients.files) {
if (cifileName === 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 } });
if (file.name.endsWith('.cif')) {
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
} else if (file.name.endsWith('.bcif')) {
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
}
} else {
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
@@ -434,16 +479,19 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
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)
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
.commit({ revertOnError: true });
const membraneParams = {
representation: params.preset.representation,
};
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
}
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
const ingredientFiles = params.ingredients.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));
@@ -451,12 +499,25 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
} else {
const file = params.source.params;
if (file === null) {
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, isBinary: false, label: file.name }, { state: { isGhost: true } });
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
}
const cellPackBuilder = cellPackJson
@@ -469,7 +530,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
await handleHivRna(plugin, packings, params.baseUrl);
for (let i = 0, il = packings.length; i < il; ++i) {
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files };
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles };
const packing = await state.build()
.to(cellPackBuilder.ref)
@@ -481,7 +542,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
representation: params.preset.representation,
};
await CellpackPackingPreset.apply(packing, packingParams, plugin);
if ( packings[i].location === 'surface' ){
if ( packings[i].location === 'surface' && params.membrane){
await loadMembrane(plugin, packings[i].name, state, params);
}
}
@@ -489,18 +550,21 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
const LoadCellPackModelParams = {
source: PD.MappedStatic('id', {
'id': PD.Select('influenza_model1.json', [
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
['hiv_lipids.bcif', 'hiv_lipids'],
['influenza_model1.json', 'influenza_model1'],
['ExosomeModel.json', 'ExosomeModel'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
] as const),
'file': PD.File({ accept: 'id' }),
'id': PD.Select('InfluenzaModel2.json', [
['blood_hiv_immature_inside.json', 'Blood HIV immature'],
['HIV_immature_model.json', 'HIV immature'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'Blood HIV'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV'],
['influenza_model1.json', 'Influenza envelope'],
['InfluenzaModel2.json', 'Influenza Complete'],
['ExosomeModel.json', 'Exosome Model'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
}, { options: [['id', 'Id'], ['file', 'File']] }),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
membrane: PD.Boolean(true),
ingredients : PD.Group({
files: PD.FileList({ accept: '.cif,.bcif,.pdb' })
}, { isExpanded: true }),
@@ -516,9 +580,5 @@ export const LoadCellPackModel = StateAction.build({
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);
}
await loadPackings(ctx, taskCtx, state, params);
}));

View File

@@ -8,7 +8,9 @@ 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';
import { CellPackGenerateColorThemeProvider } from './color/generate';
import { CellPackInfoProvider } from './property';
import { CellPackProvidedColorThemeProvider } from './color/provided';
export const CellpackPackingPresetParams = {
traceOnly: PD.Boolean(true),
@@ -40,8 +42,10 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
Object.assign(reprProps, { sizeFactor: 2 });
}
const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
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' })
};
@@ -85,6 +89,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
};
await update.commit({ revertOnError: true });
return { components, representations };
}
});

View File

@@ -5,17 +5,20 @@
*/
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
import { Structure } from '../../mol-model/structure';
import { CustomProperty } from '../../mol-model-props/common/custom-property';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Color } from '../../mol-util/color';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export type CellPackInfoValue = {
packingsCount: number
packingIndex: number
colors?: Color[]
}
const CellPackInfoParams = {
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0 }, { isHidden: true })
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0, colors: undefined }, { isHidden: true })
};
type CellPackInfoParams = PD.Values<typeof CellPackInfoParams>

View File

@@ -13,6 +13,8 @@ import { IngredientFiles } from './util';
import { Asset } from '../../mol-util/assets';
import { PluginContext } from '../../mol-plugin/context';
import { CellPackInfoProvider } from './property';
import { Structure, StructureSymmetry, Unit } from '../../mol-model/structure';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
@@ -71,10 +73,9 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
ingredientFiles[file.name] = file;
}
}
const { structure, assets } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
const { structure, assets, colors } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
info: { packingsCount: a.data.packings.length, packingIndex: params.packing }
info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
});
(cache as any).assets = assets;
@@ -94,4 +95,110 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
}
}
}
});
});
export { StructureFromAssemblies };
type StructureFromAssemblies = typeof StructureFromAssemblies
const StructureFromAssemblies = PluginStateTransform.BuiltIn({
name: 'Structure from all assemblies',
display: { name: 'Structure from all assemblies' },
from: PSO.Molecule.Model,
to: PSO.Molecule.Structure,
params: {
}
})({
canAutoUpdate({ newParams }) {
return true;
},
apply({ a, params }) {
return Task.create('Build Structure', async ctx => {
// TODO: optimze
// TODO: think of ways how to fast-track changes to this for animations
const model = a.data;
let initial_structure = Structure.ofModel(model);
const structures: Structure[] = [];
let structure: Structure = initial_structure;
// the list of asambly *?
const symmetry = ModelSymmetry.Provider.get(model);
if (symmetry && symmetry.assemblies.length !== 0){
for (const a of symmetry.assemblies) {
const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
structures.push(s);
}
const builder = Structure.Builder({ label: name });
let offsetInvariantId = 0;
for (const s of structures) {
let maxInvariantId = 0;
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId;
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
}
offsetInvariantId += maxInvariantId + 1;
}
structure = builder.getStructure();
for( let i = 0, il = structure.models.length; i < il; ++i) {
const { trajectoryInfo } = structure.models[i];
trajectoryInfo.size = il;
trajectoryInfo.index = i;
}
}
return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
});
},
dispose({ b }) {
b?.data.customPropertyDescriptors.dispose();
}
});
// export { GetAllAssamblyinOneStructure };
// type GetAllAssamblyinOneStructure = typeof GetAllAssamblyinOneStructure
// const GetAllAssamblyinOneStructure = PluginStateTransform.BuiltIn({
// name: 'get assambly from structure',
// display: { name: 'get assambly from structure' },
// isDecorator: true,
// from: PSO.Molecule.Structure,
// to: PSO.Molecule.Structure,
// params(a) {
// return { };
// }
// })({
// apply({ a, params }) {
// return Task.create('Build Structure Assemblies', async ctx => {
// // TODO: optimze
// // TODO: think of ways how to fast-track changes to this for animations
// const initial_structure = a.data;
// const structures: Structure[] = [];
// let structure: Structure = initial_structure;
// // the list of asambly *?
// const symmetry = ModelSymmetry.Provider.get(initial_structure.model);
// if (symmetry){
// if (symmetry.assemblies.length !== 0) {
// for (const a of symmetry.assemblies) {
// const s = await StructureSymmetry.buildAssembly(initial_structure, a.id!).runInContext(ctx);
// structures.push(s);
// }
// const builder = Structure.Builder({ label: name });
// let offsetInvariantId = 0;
// for (const s of structures) {
// let maxInvariantId = 0;
// for (const u of s.units) {
// const invariantId = u.invariantId + offsetInvariantId;
// if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
// builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
// }
// offsetInvariantId += maxInvariantId + 1;
// }
// structure = builder.getStructure();
// for( let i = 0, il = structure.models.length; i < il; ++i) {
// const { trajectoryInfo } = structure.models[i];
// trajectoryInfo.size = il;
// trajectoryInfo.index = i;
// }
// }
// }
// return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
// });
// }
// });

View File

@@ -7,9 +7,10 @@
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 { Model } from '../../mol-model/structure';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export namespace PDBePreferredAssembly {
export type Property = string

View File

@@ -7,9 +7,10 @@
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 { Model } from '../../mol-model/structure';
import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export namespace PDBeStructRefDomain {
export type Property = PropertyWrapper<Table<Schema['pdbe_struct_ref_domain']> | undefined>

View File

@@ -9,7 +9,7 @@ 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 { Model, 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';
@@ -22,6 +22,7 @@ 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';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
export { StructureQualityReport };

View File

@@ -8,7 +8,7 @@ import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from '../graphq
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 { 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';
@@ -19,6 +19,7 @@ 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';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
const BiologicalAssemblyNames = new Set([
'author_and_software_defined_assembly',

View File

@@ -186,12 +186,22 @@ 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));
let cage: Cage;
if (fold === 2) {
return PrismCage(polygon(4, false));
cage = PrismCage(polygon(4, false));
} else if (fold === 3) {
return WedgeCage();
cage = WedgeCage();
} else if (fold > 3) {
return PrismCage(polygon(fold, false));
cage = PrismCage(polygon(fold, false));
} else {
return;
}
if (fold % 2 === 0) {
return cage;
} else {
const m = Mat4.identity();
Mat4.rotate(m, m, 1 / fold * Math.PI / 2, Vec3.unitZ);
return transformCage(cloneCage(cage), m);
}
} else if (symbol === 'O') {
// x/y/z axes cut through order 4 vertices
@@ -237,7 +247,7 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
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];
const a2 = axes.filter(a => a.order === 2)[1];
pair = [aN, a2];
}
} else if (symbol === 'O') {
@@ -267,7 +277,19 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
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));
if (symbol.startsWith('D')) {
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) * 1.6;
}
Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, Vec3.distance(aA.start, aA.end) * 0.9));
} else {
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);
@@ -283,7 +305,6 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
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));
}
}

View File

@@ -222,12 +222,12 @@ function densityFitLabel(loci: Loci): string | undefined {
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)}`);
summary.push(`Real-Space R Z-score ${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)}`);
summary.push(`Real-Space Correlation Coefficient ${rsccCount}: ${rsccAvg.toFixed(2)}`);
}
if (summary.length) {

View File

@@ -5,7 +5,7 @@
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { CustomPropertyDescriptor, Structure, Unit } from '../../../mol-model/structure';
import { 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';
@@ -21,6 +21,7 @@ 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';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
export { ValidationReport };

View File

@@ -36,6 +36,7 @@ import { Sphere3D } from '../mol-math/geometry';
import { isDebugMode } from '../mol-util/debug';
import { CameraHelperParams } from './helper/camera-helper';
import { produce } from 'immer';
import { HandleHelper, HandleHelperParams } from './helper/handle-helper';
export const Canvas3DParams = {
camera: PD.Group({
@@ -60,7 +61,8 @@ export const Canvas3DParams = {
postprocessing: PD.Group(PostprocessingParams),
renderer: PD.Group(RendererParams),
trackball: PD.Group(TrackballControlsParams),
debug: PD.Group(DebugHelperParams)
debug: PD.Group(DebugHelperParams),
handle: PD.Group(HandleHelperParams),
};
export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
@@ -188,12 +190,13 @@ namespace Canvas3D {
const controls = TrackballControls.create(input, camera, p.trackball);
const renderer = Renderer.create(webgl, p.renderer);
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
const handleHelper = new HandleHelper(webgl, p.handle);
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, {
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, handleHelper, {
cameraHelper: p.camera.helper
});
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5);
const pickPass = new PickPass(webgl, renderer, scene, camera, handleHelper, 0.5);
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing);
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample);
@@ -205,6 +208,7 @@ namespace Canvas3D {
function getLoci(pickingId: PickingId) {
let loci: Loci = EmptyLoci;
let repr: Representation.Any = Representation.Empty;
loci = handleHelper.getLoci(pickingId);
reprRenderObjects.forEach((_, _repr) => {
const _loci = _repr.getLoci(pickingId);
if (!isEmptyLoci(_loci)) {
@@ -224,10 +228,12 @@ namespace Canvas3D {
if (repr) {
changed = repr.mark(loci, action);
} else {
changed = handleHelper.mark(loci, action);
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed) {
scene.update(void 0, true);
handleHelper.scene.update(void 0, true);
const prevPickDirty = pickPass.pickDirty;
draw(true);
pickPass.pickDirty = prevPickDirty; // marking does not change picking buffers
@@ -356,10 +362,19 @@ namespace Canvas3D {
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
reprCount.next(reprRenderObjects.size);
if (isDebugMode) consoleStats();
return true;
}
function consoleStats() {
console.table(scene.renderables.map(r => ({
drawCount: r.values.drawCount.ref.value,
instanceCount: r.values.instanceCount.ref.value,
materialId: r.materialId,
})));
}
function add(repr: Representation.Any) {
registerAutoUpdate(repr);
@@ -378,6 +393,7 @@ namespace Canvas3D {
reprRenderObjects.set(repr, newRO);
scene.update(repr.renderObjects, false);
if (isDebugMode) consoleStats();
}
function remove(repr: Representation.Any) {
@@ -388,6 +404,7 @@ namespace Canvas3D {
renderObjects.forEach(o => scene.remove(o));
reprRenderObjects.delete(repr);
scene.update(repr.renderObjects, false, true);
if (isDebugMode) consoleStats();
}
}
@@ -428,7 +445,8 @@ namespace Canvas3D {
multiSample: { ...multiSample.props },
renderer: { ...renderer.props },
trackball: { ...controls.props },
debug: { ...debugHelper.props }
debug: { ...debugHelper.props },
handle: { ...handleHelper.props },
};
}
@@ -535,11 +553,12 @@ namespace Canvas3D {
if (props.renderer) renderer.setProps(props.renderer);
if (props.trackball) controls.setProps(props.trackball);
if (props.debug) debugHelper.setProps(props.debug);
if (props.handle) handleHelper.setProps(props.handle);
requestDraw(true);
},
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props);
return new ImagePass(webgl, renderer, scene, camera, debugHelper, handleHelper, props);
},
get props() {
@@ -563,7 +582,8 @@ namespace Canvas3D {
multiSample: { ...multiSample.props },
renderer: { ...renderer.props },
trackball: { ...controls.props },
debug: { ...debugHelper.props }
debug: { ...debugHelper.props },
handle: { ...handleHelper.props },
};
},
get input() {

View File

@@ -0,0 +1,208 @@
/**
* 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 { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { Vec3, Mat4, Mat3 } 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 { 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';
import { PickingId } from '../../mol-geo/geometry/picking';
import { Camera } from '../camera';
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
import { Visual } from '../../mol-repr/visual';
import { Interval } from '../../mol-data/int';
const HandleParams = {
...Mesh.Params,
alpha: { ...Mesh.Params.alpha, defaultValue: 1 },
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 HandleParams = typeof HandleParams
type HandleProps = PD.Values<HandleParams>
export const HandleHelperParams = {
handle: PD.MappedStatic('off', {
on: PD.Group(HandleParams),
off: PD.Group({})
}, { cycle: true, description: 'Show handle tool' }),
};
export type HandleHelperParams = typeof HandleHelperParams
export type HandleHelperProps = PD.Values<HandleHelperParams>
export class HandleHelper {
scene: Scene
props: HandleHelperProps = {
handle: { name: 'off', params: {} }
}
private renderObject: GraphicsRenderObject | undefined
private _transform = Mat4();
getBoundingSphere(out: Sphere3D, instanceId: number) {
if (this.renderObject) {
Sphere3D.copy(out, this.renderObject.values.invariantBoundingSphere.ref.value);
Mat4.fromArray(this._transform, this.renderObject.values.aTransform.ref.value, instanceId * 16);
Sphere3D.transform(out, out, this._transform);
}
return out;
}
setProps(props: Partial<HandleHelperProps>) {
this.props = produce(this.props, p => {
if (props.handle !== undefined) {
p.handle.name = props.handle.name;
if (props.handle.name === 'on') {
this.scene.clear();
const params = { ...props.handle.params, scale: props.handle.params.scale * this.webgl.pixelRatio };
this.renderObject = createHandleRenderObject(params);
this.scene.add(this.renderObject);
this.scene.commit();
p.handle.params = { ...props.handle.params };
}
}
});
}
get isEnabled() {
return this.props.handle.name === 'on';
}
// TODO could be a lists of position/rotation if we want to show more than one handle tool,
// they would be distingishable by their instanceId
update(camera: Camera, position: Vec3, rotation: Mat3) {
if (!this.renderObject) return;
Mat4.setTranslation(this.renderObject.values.aTransform.ref.value as unknown as Mat4, position);
Mat4.fromMat3(this.renderObject.values.aTransform.ref.value as unknown as Mat4, rotation);
// TODO make invariant to camera scaling by adjusting renderObject transform
ValueCell.update(this.renderObject.values.aTransform, this.renderObject.values.aTransform.ref.value);
this.scene.update([this.renderObject], true);
}
getLoci(pickingId: PickingId) {
const { objectId, groupId, instanceId } = pickingId;
if (!this.renderObject || objectId !== this.renderObject.id) return EmptyLoci;
return HandleLoci(this, groupId, instanceId);
}
private eachGroup = (loci: Loci, apply: (interval: Interval) => boolean): boolean => {
if (!this.renderObject) return false;
if (!isHandleLoci(loci)) return false;
let changed = false;
const groupCount = this.renderObject.values.uGroupCount.ref.value;
const { elements } = loci;
for (const { groupId, instanceId } of elements) {
const idx = instanceId * groupCount + groupId;
if (apply(Interval.ofSingleton(idx))) changed = true;
}
return changed;
}
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
if (!isHandleLoci(loci)) return false;
if (loci.data !== this) return false;
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
}
constructor(private webgl: WebGLContext, props: Partial<HandleHelperProps> = {}) {
this.scene = Scene.create(webgl);
this.setProps(props);
}
}
function createHandleMesh(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 = HandleGroup.TranslateScreenXY;
addSphere(state, Vec3.origin, radius * 3, 2);
state.currentGroup = HandleGroup.TranslateObjectX;
addSphere(state, x, radius, 2);
addCylinder(state, Vec3.origin, x, 1, cylinderProps);
state.currentGroup = HandleGroup.TranslateObjectY;
addSphere(state, y, radius, 2);
addCylinder(state, Vec3.origin, y, 1, cylinderProps);
state.currentGroup = HandleGroup.TranslateObjectZ;
addSphere(state, z, radius, 2);
addCylinder(state, Vec3.origin, z, 1, cylinderProps);
// TODO add more helper geometries for the other HandleGroup options
// TODO add props to create subset of geometries
return MeshBuilder.getMesh(state);
}
export const HandleGroup = {
None: 0,
TranslateScreenXY: 1,
// TranslateScreenZ: 2,
TranslateObjectX: 3,
TranslateObjectY: 4,
TranslateObjectZ: 5,
// TranslateObjectXY: 6,
// TranslateObjectXZ: 7,
// TranslateObjectYZ: 8,
// RotateScreenZ: 9,
// RotateObjectX: 10,
// RotateObjectY: 11,
// RotateObjectZ: 12,
} as const;
function HandleLoci(handleHelper: HandleHelper, groupId: number, instanceId: number) {
return DataLoci('handle', handleHelper, [{ groupId, instanceId }],
(boundingSphere: Sphere3D) => handleHelper.getBoundingSphere(boundingSphere, instanceId),
() => `Handle Helper | Group Id ${groupId} | Instance Id ${instanceId}`);
}
export type HandleLoci = ReturnType<typeof HandleLoci>
export function isHandleLoci(x: Loci): x is HandleLoci {
return x.kind === 'data-loci' && x.tag === 'handle';
}
function getHandleShape(props: HandleProps, shape?: Shape<Mesh>) {
const scale = 10 * props.scale;
const mesh = createHandleMesh(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 HandleGroup.TranslateObjectX: return props.colorX;
case HandleGroup.TranslateObjectY: return props.colorY;
case HandleGroup.TranslateObjectZ: return props.colorZ;
default: return ColorNames.grey;
}
};
return Shape.create('handle', {}, mesh, getColor, () => 1, () => '');
}
function createHandleRenderObject(props: HandleProps) {
const shape = getHandleShape(props);
return Shape.createRenderObject(shape, props);
}

View File

@@ -13,6 +13,7 @@ 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';
import { HandleHelper } from '../helper/handle-helper';
export const DrawPassParams = {
cameraHelper: PD.Group(CameraHelperParams)
@@ -29,7 +30,7 @@ export class DrawPass {
private depthTarget: RenderTarget | null
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, props: Partial<DrawPassProps> = {}) {
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, private handleHelper: HandleHelper, props: Partial<DrawPassProps> = {}) {
const { gl, extensions, resources } = webgl;
const width = gl.drawingBufferWidth;
const height = gl.drawingBufferHeight;
@@ -89,12 +90,15 @@ export class DrawPass {
}
private renderInternal(variant: 'color' | 'depth', transparentBackground: boolean) {
const { renderer, scene, camera, debugHelper, cameraHelper } = this;
const { renderer, scene, camera, debugHelper, cameraHelper, handleHelper } = this;
renderer.render(scene, camera, variant, true, transparentBackground);
if (debugHelper.isEnabled) {
debugHelper.syncVisibility();
renderer.render(debugHelper.scene, camera, variant, false, transparentBackground);
}
if (handleHelper.isEnabled) {
renderer.render(handleHelper.scene, camera, variant, false, transparentBackground);
}
if (cameraHelper.isEnabled) {
cameraHelper.update(camera);
renderer.render(cameraHelper.scene, cameraHelper.camera, variant, false, transparentBackground);

View File

@@ -15,6 +15,7 @@ import { PostprocessingPass, PostprocessingParams } from './postprocessing';
import { MultiSamplePass, MultiSampleParams } from './multi-sample';
import { Camera } from '../camera';
import { Viewport } from '../camera/util';
import { HandleHelper } from '../helper/handle-helper';
export const ImageParams = {
transparentBackground: PD.Boolean(false),
@@ -40,12 +41,12 @@ export class ImagePass {
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>) {
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, handleHelper: HandleHelper, props: Partial<ImageProps>) {
const p = { ...PD.getDefaultValues(ImageParams), ...props };
this._transparentBackground = p.transparentBackground;
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, p.drawPass);
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, handleHelper, 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);

View File

@@ -11,6 +11,7 @@ import Scene from '../../mol-gl/scene';
import { PickingId } from '../../mol-geo/geometry/picking';
import { decodeFloatRGB } from '../../mol-util/float-packing';
import { Camera } from '../camera';
import { HandleHelper } from '../helper/handle-helper';
export class PickPass {
pickDirty = true
@@ -27,7 +28,7 @@ export class PickPass {
private pickWidth: number
private pickHeight: number
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number) {
const { gl } = webgl;
const width = gl.drawingBufferWidth;
const height = gl.drawingBufferHeight;
@@ -65,14 +66,18 @@ export class PickPass {
}
render() {
const { renderer, scene, camera } = this;
const { renderer, scene, camera, handleHelper: { scene: handleScene } } = this;
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
this.objectPickTarget.bind();
renderer.render(scene, camera, 'pickObject', true, false);
renderer.render(handleScene, camera, 'pickObject', false, false);
this.instancePickTarget.bind();
renderer.render(scene, camera, 'pickInstance', true, false);
renderer.render(handleScene, camera, 'pickInstance', false, false);
this.groupPickTarget.bind();
renderer.render(scene, camera, 'pickGroup', true, false);
renderer.render(handleScene, camera, 'pickGroup', false, false);
this.pickDirty = false;
}

View File

@@ -59,7 +59,7 @@ export const PostprocessingParams = {
outline: PD.MappedStatic('off', {
on: PD.Group({
scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
threshold: PD.Numeric(0.8, { min: 0, max: 5, step: 0.01 }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Draw outline around 3D objects' })

View File

@@ -207,5 +207,6 @@ export namespace Points {
!props.pointFilledCircle ||
(props.pointFilledCircle && props.pointEdgeBleach === 0)
);
state.writeDepth = state.opaque;
}
}

View File

@@ -283,6 +283,7 @@ export namespace Text {
BaseGeometry.updateRenderableState(state, props);
state.pickable = false;
state.opaque = false;
state.writeDepth = true;
}
}

View File

@@ -44,9 +44,6 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
if (values.uAlpha && values.alpha) {
ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1));
}
if (values.uPickable) {
ValueCell.updateIfChanged(values.uPickable, state.pickable ? 1 : 0);
}
renderItem.render(variant);
},
getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant),

View File

@@ -73,7 +73,6 @@ export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: Di
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0),
};
const shaderCode = DirectVolumeShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -33,7 +33,6 @@ export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValu
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...ImageSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0),
};
const shaderCode = ImageShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -29,7 +29,6 @@ export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValu
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = LinesShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -28,7 +28,6 @@ export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = MeshShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -26,7 +26,6 @@ export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsVa
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = PointsShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -187,7 +187,6 @@ export type GlobalUniformValues = Values<GlobalUniformSchema> // { [k in keyof G
export const InternalSchema = {
uObjectId: UniformSpec('i'),
uPickable: UniformSpec('i', true),
} as const;
export type InternalSchema = typeof InternalSchema
export type InternalValues = { [k in keyof InternalSchema]: ValueCell<any> }

View File

@@ -29,7 +29,6 @@ export function SpheresRenderable(ctx: WebGLContext, id: number, values: Spheres
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...SpheresSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = SpheresShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -38,7 +38,6 @@ export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = TextShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -31,7 +31,6 @@ export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: Tex
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextureMeshSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
uPickable: ValueCell.create(state.pickable ? 1 : 0)
};
const shaderCode = MeshShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

View File

@@ -173,51 +173,53 @@ namespace Renderer {
let globalUniformsNeedUpdate = true;
const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => {
if (!r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
return;
}
const program = r.getProgram(variant);
if (r.state.visible) {
if (state.currentProgramId !== program.id) {
// console.log('new program')
globalUniformsNeedUpdate = true;
program.use();
}
if (state.currentProgramId !== program.id) {
// console.log('new program')
globalUniformsNeedUpdate = true;
program.use();
}
if (globalUniformsNeedUpdate) {
// console.log('globalUniformsNeedUpdate')
program.setUniforms(globalUniformList);
globalUniformsNeedUpdate = false;
}
if (globalUniformsNeedUpdate) {
// console.log('globalUniformsNeedUpdate')
program.setUniforms(globalUniformList);
globalUniformsNeedUpdate = false;
}
if (r.values.dDoubleSided) {
if (r.values.dDoubleSided.ref.value) {
state.disable(gl.CULL_FACE);
} else {
state.enable(gl.CULL_FACE);
}
} else {
// webgl default
if (r.values.dDoubleSided) {
if (r.values.dDoubleSided.ref.value) {
state.disable(gl.CULL_FACE);
}
if (r.values.dFlipSided) {
if (r.values.dFlipSided.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
} else {
// webgl default
state.enable(gl.CULL_FACE);
}
} else {
// webgl default
state.disable(gl.CULL_FACE);
}
if (r.values.dFlipSided) {
if (r.values.dFlipSided.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
if (variant === 'color') {
state.depthMask(r.state.writeDepth);
}
r.render(variant);
} else {
// webgl default
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
if (variant === 'color') {
state.depthMask(r.state.writeDepth);
}
r.render(variant);
};
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => {

View File

@@ -1,13 +1,10 @@
export default `
// only mark elements with an alpha above the picking threshold
if (gl_FragColor.a >= uPickingAlphaThreshold) {
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
if (marker > 0.1) {
if (intMod(marker, 2.0) > 0.1) {
gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3);
} else {
gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3);
}
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
if (marker > 0.1) {
if (intMod(marker, 2.0) > 0.1) {
gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3);
} else {
gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3);
}
}
`;

View File

@@ -34,7 +34,7 @@ export default `
if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
#endif
#elif defined(dRenderVariant_pick)
vec4 material = uPickable == 1 ? vColor : vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
vec4 material = vColor;
#elif defined(dRenderVariant_depth)
#ifdef enabledFragDepth
vec4 material = packDepthToRGBA(gl_FragDepthEXT);

View File

@@ -21,7 +21,6 @@ uniform vec3 uFogColor;
uniform float uAlpha;
uniform float uPickingAlphaThreshold;
uniform int uPickable;
uniform int uTransparentBackground;
uniform float uInteriorDarkening;

View File

@@ -28,7 +28,6 @@ uniform sampler2D tMarker;
uniform float uAlpha;
uniform float uPickingAlphaThreshold;
uniform int uPickable;
#if defined(dGridTexType_2d)
precision highp sampler2D;
@@ -117,8 +116,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 viewDir) {
#if defined(dRenderVariant_pick)
if (uAlpha < uPickingAlphaThreshold)
discard; // ignore so the element below can be picked
if (uPickable == 0)
return vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
#endif
#if defined(dRenderVariant_pickObject)

View File

@@ -99,18 +99,14 @@ void main() {
if (imageData.a < 0.3)
discard;
if (uPickable == 1) {
#if defined(dRenderVariant_pickObject)
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
float group = texture2D(tGroupTex, vUv).r;
gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
#endif
} else {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
}
#if defined(dRenderVariant_pickObject)
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
float group = texture2D(tGroupTex, vUv).r;
gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
#endif
#elif defined(dRenderVariant_depth)
if (imageData.a < 0.05)
discard;

View File

@@ -1,5 +1,5 @@
import Mol2 from '../mol2/parser';
import { parseMol2 } from '../mol2/parser';
const Mol2String = `@<TRIPOS>MOLECULE
5816
@@ -246,7 +246,7 @@ GASTEIGER
describe('mol2 reader', () => {
it('basic', async () => {
const parsed = await Mol2(Mol2String).run();
const parsed = await parseMol2(Mol2String, '').run();
if (parsed.isError) {
throw new Error(parsed.message);
}
@@ -297,7 +297,7 @@ describe('mol2 reader', () => {
});
it('multiblocks', async () => {
const parsed = await Mol2(Mol2StringMultiBlocks).run();
const parsed = await parseMol2(Mol2StringMultiBlocks, '').run();
if (parsed.isError) {
throw new Error(parsed.message);
}
@@ -348,7 +348,7 @@ describe('mol2 reader', () => {
});
it('minimal', async () => {
const parsed = await Mol2(Mol2StringMinimal).run();
const parsed = await parseMol2(Mol2StringMinimal, '').run();
if (parsed.isError) {
throw new Error(parsed.message);
}

View File

@@ -54,7 +54,7 @@ const reWhitespace = /\s+/g;
function handleMolecule(state: State) {
const { tokenizer, molecule } = state;
while (getTokenString(tokenizer) !== '@<TRIPOS>MOLECULE') {
while (getTokenString(tokenizer) !== '@<TRIPOS>MOLECULE' && tokenizer.position < tokenizer.data.length) {
markLine(tokenizer);
}
@@ -101,7 +101,7 @@ async function handleAtoms(state: State): Promise<Schema.Mol2Atoms> {
let hasStatus_bit = false;
// skip empty lines and '@<TRIPOS>ATOM'
while (getTokenString(tokenizer) !== '@<TRIPOS>ATOM') {
while (getTokenString(tokenizer) !== '@<TRIPOS>ATOM' && tokenizer.position < tokenizer.data.length) {
markLine(tokenizer);
}
@@ -243,7 +243,7 @@ async function handleBonds(state: State): Promise<Schema.Mol2Bonds> {
const { tokenizer, molecule } = state;
let hasStatus_bit = false;
while (getTokenString(tokenizer) !== '@<TRIPOS>BOND') {
while (getTokenString(tokenizer) !== '@<TRIPOS>BOND' && tokenizer.position < tokenizer.data.length) {
markLine(tokenizer);
}
@@ -324,7 +324,7 @@ async function handleBonds(state: State): Promise<Schema.Mol2Bonds> {
return ret;
}
async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<Schema.Mol2File>> {
async function parseInternal(ctx: RuntimeContext, data: string, name: string): Promise<Result<Schema.Mol2File>> {
const tokenizer = Tokenizer(data);
ctx.update({ message: 'Parsing...', current: 0, max: data.length });
@@ -335,16 +335,15 @@ async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<
const atoms = await handleAtoms(state);
const bonds = await handleBonds(state);
structures.push({ molecule: state.molecule, atoms, bonds });
skipWhitespace(tokenizer);
}
const result: Schema.Mol2File = { structures };
const result: Schema.Mol2File = { name, structures };
return Result.success(result);
}
export function parse(data: string) {
export function parseMol2(data: string, name: string) {
return Task.create<Result<Schema.Mol2File>>('Parse MOL2', async ctx => {
return await parseInternal(data, ctx);
return await parseInternal(ctx, data, name);
});
}
export default parse;
}

View File

@@ -63,5 +63,6 @@ export interface Mol2Structure {
}
export interface Mol2File {
name: string
structures: Mol2Structure[]
}

View File

@@ -6,7 +6,7 @@
import { Model } from '../../mol-model/structure/model';
import { Task } from '../../mol-task';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { Column, Table } from '../../mol-data/db';
import { EntityBuilder } from './common/entity';
import { File3DG } from '../../mol-io/reader/3dg/parser';

View File

@@ -16,7 +16,7 @@ import { ElementSymbol } from '../../../mol-model/structure/model/types';
import { Entities } from '../../../mol-model/structure/model/properties/common';
import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived';
import { AtomSite } from './schema';
import { ModelFormat } from '../format';
import { ModelFormat } from '../../format';
import { SymmetryOperator } from '../../../mol-math/geometry';
import { MmcifFormat } from '../mmcif';
import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';

View File

@@ -10,12 +10,12 @@ import { RuntimeContext } from '../../../mol-task';
import UUID from '../../../mol-util/uuid';
import { Model } from '../../../mol-model/structure/model/model';
import { Entities } from '../../../mol-model/structure/model/properties/common';
import { CustomProperties } from '../../../mol-model/structure';
import { CustomProperties } from '../../../mol-model/custom-property';
import { getAtomicHierarchyAndConformation } from './atomic';
import { getCoarse, EmptyCoarse, CoarseData } from './coarse';
import { getSequence } from './sequence';
import { sortAtomSite } from './sort';
import { ModelFormat } from '../format';
import { ModelFormat } from '../../format';
import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges';
import { AtomSite, BasicData } from './schema';
import { getProperties } from './properties';

View File

@@ -12,7 +12,7 @@ import { createModels } from './basic/parser';
import { BasicSchema, createBasic } from './basic/schema';
import { ComponentBuilder } from './common/component';
import { EntityBuilder } from './common/entity';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { CifCore_Database } from '../../mol-io/reader/cif/schema/cif-core';
import { CifFrame, CIF } from '../../mol-io/reader/cif';
import { Spacegroup, SpacegroupCell } from '../../mol-math/geometry';

View File

@@ -4,8 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor, Model } from '../../../mol-model/structure';
import { ModelFormat } from '../format';
import { Model } from '../../../mol-model/structure';
import { ModelFormat } from '../../format';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
class FormatRegistry<T> {
private map = new Map<ModelFormat['kind'], (model: Model) => T | undefined>()

View File

@@ -12,7 +12,7 @@ import { createModels } from './basic/parser';
import { BasicSchema, createBasic } from './basic/schema';
import { ComponentBuilder } from './common/component';
import { EntityBuilder } from './common/entity';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { CubeFile } from '../../mol-io/reader/cube/parser';
async function getModels(cube: CubeFile, ctx: RuntimeContext): Promise<Model[]> {

View File

@@ -6,7 +6,7 @@
import { Model } from '../../mol-model/structure/model';
import { Task } from '../../mol-task';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { GroFile, GroAtoms } from '../../mol-io/reader/gro/schema';
import { Column, Table } from '../../mol-data/db';
import { guessElementSymbolString } from './util';

View File

@@ -7,7 +7,7 @@
import { Model } from '../../mol-model/structure/model/model';
import { Task } from '../../mol-task';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { CifFrame, CIF } from '../../mol-io/reader/cif';
import { mmCIF_Database } from '../../mol-io/reader/cif/schema/mmcif';
import { createModels } from './basic/parser';

View File

@@ -14,7 +14,7 @@ import { createModels } from './basic/parser';
import { BasicSchema, createBasic } from './basic/schema';
import { ComponentBuilder } from './common/component';
import { EntityBuilder } from './common/entity';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { IndexPairBonds } from './property/bonds/index-pair';
async function getModels(mol: MolFile, ctx: RuntimeContext): Promise<Model[]> {

View File

@@ -0,0 +1,98 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Column, Table } from '../../mol-data/db';
import { Model } from '../../mol-model/structure/model';
import { MoleculeType } from '../../mol-model/structure/model/types';
import { RuntimeContext, Task } from '../../mol-task';
import { createModels } from './basic/parser';
import { BasicSchema, createBasic } from './basic/schema';
import { ComponentBuilder } from './common/component';
import { EntityBuilder } from './common/entity';
import { ModelFormat } from '../format';
import { IndexPairBonds } from './property/bonds/index-pair';
import { Mol2File } from '../../mol-io/reader/mol2/schema';
async function getModels(mol2: Mol2File, ctx: RuntimeContext): Promise<Model[]> {
const models: Model[] = [];
for (let i = 0, il = mol2.structures.length; i < il; ++i) {
const { atoms, bonds } = mol2.structures[i];
const A = Column.ofConst('A', atoms.count, Column.Schema.str);
const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
auth_asym_id: A,
auth_atom_id: Column.asArrayColumn(atoms.atom_type),
auth_comp_id: atoms.subst_name,
auth_seq_id: atoms.subst_id,
Cartn_x: Column.asArrayColumn(atoms.x, Float32Array),
Cartn_y: Column.asArrayColumn(atoms.y, Float32Array),
Cartn_z: Column.asArrayColumn(atoms.z, Float32Array),
id: Column.asArrayColumn(atoms.atom_id),
label_asym_id: A,
label_atom_id: Column.asArrayColumn(atoms.atom_type),
label_comp_id: atoms.subst_name,
label_seq_id: atoms.subst_id,
label_entity_id: Column.ofConst('1', atoms.count, Column.Schema.str),
occupancy: Column.ofConst(1, atoms.count, Column.Schema.float),
type_symbol: Column.asArrayColumn(atoms.atom_name),
pdbx_PDB_model_num: Column.ofConst(i, atoms.count, Column.Schema.int),
}, atoms.count);
const entityBuilder = new EntityBuilder();
entityBuilder.setNames([['MOL', 'Unknown Entity']]);
entityBuilder.getEntityId('MOL', MoleculeType.Unknown, 'A');
const componentBuilder = new ComponentBuilder(atoms.subst_id, atoms.atom_name);
for (let i = 0, il = atoms.subst_name.rowCount; i < il; ++i) {
componentBuilder.add(atoms.subst_name.value(i), i);
}
const basics = createBasic({
entity: entityBuilder.getEntityTable(),
chem_comp: componentBuilder.getChemCompTable(),
atom_site
});
const _models = await createModels(basics, Mol2Format.create(mol2), ctx);
if (_models.length > 0) {
const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array));
const indexB = Column.ofIntArray(Column.mapToArray(bonds.target_atom_id, x => x - 1, Int32Array));
const order = Column.ofIntArray(Column.mapToArray(bonds.bond_type, x => x === 'ar' ? 1 : parseInt(x), Int8Array));
const pairBonds = IndexPairBonds.fromData({ pairs: { indexA, indexB, order }, count: bonds.count });
IndexPairBonds.Provider.set(_models[0], pairBonds);
models.push(_models[0]);
}
}
return models;
}
//
export { Mol2Format };
type Mol2Format = ModelFormat<Mol2File>
namespace Mol2Format {
export function is(x: ModelFormat): x is Mol2Format {
return x.kind === 'mol2';
}
export function create(mol2: Mol2File): Mol2Format {
return { kind: 'mol2', name: mol2.name, data: mol2 };
}
}
export function trajectoryFromMol2(mol2: Mol2File): Task<Model.Trajectory> {
return Task.create('Parse MOL2', ctx => getModels(mol2, ctx));
}

View File

@@ -5,7 +5,7 @@
*/
import { Table, Column } from '../../../mol-data/db';
import { CustomPropertyDescriptor } from '../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { CifWriter } from '../../../mol-io/writer/cif';
import { FormatPropertyProvider } from '../common/property';

View File

@@ -7,7 +7,7 @@
import { Model } from '../../../../mol-model/structure/model/model';
import { BondType } from '../../../../mol-model/structure/model/types';
import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
import { CifWriter } from '../../../../mol-io/writer/cif';
import { Table } from '../../../../mol-data/db';

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
import { IntAdjacencyGraph } from '../../../../mol-math/graph';
import { Column } from '../../../../mol-data/db';
import { FormatPropertyProvider } from '../../common/property';

View File

@@ -9,7 +9,7 @@ import { Model } from '../../../../mol-model/structure/model/model';
import { Structure } from '../../../../mol-model/structure';
import { BondType } from '../../../../mol-model/structure/model/types';
import { Column, Table } from '../../../../mol-data/db';
import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
import { SortedArray } from '../../../../mol-data/int';
import { CifWriter } from '../../../../mol-io/writer/cif';

View File

@@ -13,7 +13,7 @@ import { SecondaryStructure } from '../../../mol-model/structure/model/propertie
import { Column, Table } from '../../../mol-data/db';
import { ChainIndex, ResidueIndex } from '../../../mol-model/structure/model/indexing';
import { FormatPropertyProvider } from '../common/property';
import { CustomPropertyDescriptor } from '../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
export { ModelSecondaryStructure };

View File

@@ -10,7 +10,7 @@ import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/
import { Tensor, Vec3, Mat3 } from '../../../mol-math/linear-algebra';
import { Symmetry } from '../../../mol-model/structure/model/properties/symmetry';
import { createAssemblies } from './assembly';
import { CustomPropertyDescriptor } from '../../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
import { FormatPropertyProvider } from '../common/property';
import { Table } from '../../../mol-data/db';

View File

@@ -12,7 +12,7 @@ import { guessElementSymbolString } from './util';
import { MoleculeType, getMoleculeType } from '../../mol-model/structure/model/types';
import { getChainId } from './common/util';
import { Task } from '../../mol-task';
import { ModelFormat } from './format';
import { ModelFormat } from '../format';
import { Topology } from '../../mol-model/structure/topology/topology';
import { createBasic, BasicSchema } from './basic/schema';

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { VolumeData } from '../../mol-model/volume/data';
import { Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -13,6 +13,8 @@ import { degToRad } from '../../mol-math/misc';
import { getCcp4ValueType } from '../../mol-io/reader/ccp4/parser';
import { TypedArrayValueType } from '../../mol-io/common/typed-array';
import { arrayMin, arrayRms, arrayMean, arrayMax } from '../../mol-util/array';
import { ModelFormat } from '../format';
import { CustomProperties } from '../../mol-model/custom-property';
/** When available (e.g. in MRC files) use ORIGIN records instead of N[CRS]START */
export function getCcp4Origin(header: Ccp4Header): Vec3 {
@@ -38,8 +40,8 @@ function getTypedArrayCtor(header: Ccp4Header) {
throw Error(`${valueType} is not a supported value format.`);
}
export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async ctx => {
export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<Volume> {
return Task.create<Volume>('Create Volume', async ctx => {
const { header, values } = source;
const size = Vec3.create(header.xLength, header.yLength, header.zLength);
if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -68,14 +70,35 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
return {
label: params?.label,
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
data,
dataStats: {
min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
}
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
cells: data,
stats: {
min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
},
},
sourceData: Ccp4Format.create(source),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
});
}
//
export { Ccp4Format };
type Ccp4Format = ModelFormat<Ccp4File>
namespace Ccp4Format {
export function is(x: ModelFormat): x is Ccp4Format {
return x.kind === 'ccp4';
}
export function create(ccp4: Ccp4File): Ccp4Format {
return { kind: 'ccp4', name: ccp4.name, data: ccp4 };
}
}

View File

@@ -6,12 +6,14 @@
import { CubeFile } from '../../mol-io/reader/cube/parser';
import { Mat4, Tensor } from '../../mol-math/linear-algebra';
import { VolumeData } from '../../mol-model/volume/data';
import { Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
import { ModelFormat } from '../format';
import { CustomProperties } from '../../mol-model/custom-property';
export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async () => {
export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<Volume> {
return Task.create<Volume>('Create Volume', async () => {
const { header, values: sourceValues } = source;
const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
@@ -44,14 +46,35 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
return {
label: params?.label,
transform: { kind: 'matrix', matrix },
data,
dataStats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: arrayRms(values)
}
grid: {
transform: { kind: 'matrix', matrix },
cells: data,
stats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: arrayRms(values)
},
},
sourceData: CubeFormat.create(source),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
});
}
//
export { CubeFormat };
type CubeFormat = ModelFormat<CubeFile>
namespace CubeFormat {
export function is(x: ModelFormat): x is CubeFormat {
return x.kind === 'cube';
}
export function create(cube: CubeFile): CubeFormat {
return { kind: 'cube', name: cube.name, data: cube };
}
}

View File

@@ -5,13 +5,15 @@
*/
import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
import { VolumeData } from '../../mol-model/volume/data';
import { Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
import { ModelFormat } from '../format';
import { CustomProperties } from '../../mol-model/custom-property';
function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async ctx => {
export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<Volume> {
return Task.create<Volume>('Create Volume', async ctx => {
const { volume_data_3d_info: info, volume_data_3d: values } = source;
const cell = SpacegroupCell.create(
info.spacegroup_number.value(0),
@@ -34,16 +36,35 @@ function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<
const dimensions = Vec3.ofArray(normalizeOrder(info.dimensions.value(0)));
return {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
data,
dataStats: {
min: info.min_sampled.value(0),
max: info.max_sampled.value(0),
mean: info.mean_sampled.value(0),
sigma: info.sigma_sampled.value(0)
}
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
cells: data,
stats: {
min: info.min_sampled.value(0),
max: info.max_sampled.value(0),
mean: info.mean_sampled.value(0),
sigma: info.sigma_sampled.value(0)
},
},
sourceData: DscifFormat.create(source),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
});
}
export { volumeFromDensityServerData };
//
export { DscifFormat };
type DscifFormat = ModelFormat<DensityServer_Data_Database>
namespace DscifFormat {
export function is(x: ModelFormat): x is DscifFormat {
return x.kind === 'dscif';
}
export function create(dscif: DensityServer_Data_Database): DscifFormat {
return { kind: 'dscif', name: dscif._name, data: dscif };
}
}

View File

@@ -4,16 +4,18 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { VolumeData } from '../../mol-model/volume/data';
import { Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
import { degToRad } from '../../mol-math/misc';
import { Dsn6File } from '../../mol-io/reader/dsn6/schema';
import { arrayMin, arrayMax, arrayMean, arrayRms } from '../../mol-util/array';
import { ModelFormat } from '../format';
import { CustomProperties } from '../../mol-model/custom-property';
function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async ctx => {
export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<Volume> {
return Task.create<Volume>('Create Volume', async ctx => {
const { header, values } = source;
const size = Vec3.create(header.xlen, header.ylen, header.zlen);
if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -33,16 +35,35 @@ function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: s
return {
label: params?.label,
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
data,
dataStats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
}
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
cells: data,
stats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
},
},
sourceData: Dsn6Format.create(source),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
});
}
export { volumeFromDsn6 };
//
export { Dsn6Format };
type Dsn6Format = ModelFormat<Dsn6File>
namespace Dsn6Format {
export function is(x: ModelFormat): x is Dsn6Format {
return x.kind === 'dsn6';
}
export function create(dsn6: Dsn6File): Dsn6Format {
return { kind: 'dsn6', name: dsn6.name, data: dsn6 };
}
}

View File

@@ -6,12 +6,14 @@
import { DxFile } from '../../mol-io/reader/dx/parser';
import { Mat4, Tensor } from '../../mol-math/linear-algebra';
import { VolumeData } from '../../mol-model/volume/data';
import { Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
import { ModelFormat } from '../format';
import { CustomProperties } from '../../mol-model/custom-property';
export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async () => {
export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<Volume> {
return Task.create<Volume>('Create Volume', async () => {
const { header, values } = source;
const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
const data = Tensor.create(space, Tensor.Data1(values));
@@ -21,14 +23,35 @@ export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<
return {
label: params?.label,
transform: { kind: 'matrix', matrix },
data,
dataStats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: arrayRms(values)
}
grid: {
transform: { kind: 'matrix', matrix },
cells: data,
stats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: arrayRms(values)
},
},
sourceData: DxFormat.create(source),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
});
}
//
export { DxFormat };
type DxFormat = ModelFormat<DxFile>
namespace DxFormat {
export function is(x: ModelFormat): x is DxFormat {
return x.kind === 'dx';
}
export function create(dx: DxFile): DxFormat {
return { kind: 'dx', name: dx.name, data: dx };
}
}

View File

@@ -5,7 +5,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ElementIndex, Model, CustomPropertyDescriptor } from '../../mol-model/structure';
import { ElementIndex, Model } from '../../mol-model/structure';
import { StructureElement } from '../../mol-model/structure/structure';
import { Location } from '../../mol-model/location';
import { ThemeDataContext } from '../../mol-theme/theme';
@@ -16,6 +16,7 @@ import { OrderedSet } from '../../mol-data/int';
import { CustomModelProperty } from './custom-model-property';
import { CustomProperty } from './custom-property';
import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export { CustomElementProperty };

View File

@@ -4,10 +4,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor, Model } from '../../mol-model/structure';
import { Model } from '../../mol-model/structure';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ValueBox } from '../../mol-util';
import { CustomProperty } from './custom-property';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export { CustomModelProperty };

View File

@@ -5,7 +5,7 @@
*/
import { RuntimeContext } from '../../mol-task';
import { CustomPropertyDescriptor } from '../../mol-model/structure';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ValueBox } from '../../mol-util';
import { OrderedMap } from 'immutable';

View File

@@ -4,10 +4,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
import { Structure } from '../../mol-model/structure';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ValueBox } from '../../mol-util';
import { CustomProperty } from './custom-property';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export { CustomStructureProperty };

View File

@@ -7,12 +7,13 @@
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ShrakeRupleyComputationParams, AccessibleSurfaceArea } from './accessible-surface-area/shrake-rupley';
import { Structure, CustomPropertyDescriptor, Unit } from '../../mol-model/structure';
import { Structure, Unit } from '../../mol-model/structure';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
import { CustomPropSymbol } from '../../mol-script/language/symbol';
import Type from '../../mol-script/language/type';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export const AccessibleSurfaceAreaParams = {
...ShrakeRupleyComputationParams

View File

@@ -4,11 +4,12 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
import { Structure } from '../../mol-model/structure';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { computeInteractions, Interactions, InteractionsParams as _InteractionsParams } from './interactions/interactions';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export const InteractionsParams = {
..._InteractionsParams

View File

@@ -12,7 +12,7 @@ import { Unit } from '../../mol-model/structure/structure';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
import { CustomPropertyDescriptor } from '../../mol-model/structure/common/custom-property';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
import { Model } from '../../mol-model/structure/model';
function getSecondaryStructureParams(data?: Structure) {

View File

@@ -4,11 +4,12 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
import { Structure } from '../../mol-model/structure';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { calcValenceModel, ValenceModel, ValenceModelParams as _ValenceModelParams } from './chemistry/valence-model';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
export const ValenceModelParams = {
..._ValenceModelParams

View File

@@ -7,9 +7,10 @@
import { Model } from '../../../mol-model/structure/model/model';
import { Table } from '../../../mol-data/db';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { Unit, CustomPropertyDescriptor } from '../../../mol-model/structure';
import { Unit } from '../../../mol-model/structure';
import { ElementIndex } from '../../../mol-model/structure/model/indexing';
import { FormatPropertyProvider } from '../../../mol-model-formats/structure/common/property';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
export { ModelCrossLinkRestraint };

View File

@@ -5,7 +5,7 @@
*/
import { ModelCrossLinkRestraint } from './format';
import { Unit, StructureElement, Structure, CustomPropertyDescriptor, Bond} from '../../../mol-model/structure';
import { Unit, StructureElement, Structure, Bond} from '../../../mol-model/structure';
import { PairRestraints, PairRestraint } from '../pair-restraints';
import { CustomStructureProperty } from '../../common/custom-structure-property';
import { CustomProperty } from '../../common/custom-property';
@@ -15,6 +15,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
import { bondLabel } from '../../../mol-theme/label';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
export type CrossLinkRestraintValue = PairRestraints<CrossLinkRestraint>

View File

@@ -5,11 +5,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CifWriter } from '../../../mol-io/writer/cif';
import { CifExportContext } from '../export/mmcif';
import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
import { UUID } from '../../../mol-util';
import { Asset } from '../../../mol-util/assets';
import { CifWriter } from '../mol-io/writer/cif';
import { CifExportContext } from './structure/export/mmcif';
import { QuerySymbolRuntime } from '../mol-script/runtime/query/compiler';
import { UUID } from '../mol-util';
import { Asset } from '../mol-util/assets';
export { CustomPropertyDescriptor, CustomProperties };

View File

@@ -255,11 +255,12 @@ namespace Loci {
};
export type Granularity = keyof typeof Granularity
export const GranularityOptions = ParamDefinition.objectToOptions(Granularity, k => {
if (k.indexOf('Instances') > 0) return [stringToWords(k), 'With Symmetry'];
switch (k) {
case 'element': return'Atom/Coarse Element';
case 'elementInstances': return ['Atom/Coarse Element Instances', 'With Symmetry'];
case 'structure': return'Structure/Shape';
default: return stringToWords(k);
default: return k.indexOf('Instances')
? [stringToWords(k), 'With Symmetry'] : stringToWords(k);
}
});

View File

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { align } from '../alignment';
describe('Alignment', () => {
it('basic', () => {
// 3PQR: Rhodopsin
const seqA = 'MNGTEGPNFYVPFSNKTGVVRSPFEAPQYYLAEPWQFSMLAAYMFLLIMLGFPINFLTLYVTVQHKKLRTPLNYILLNLAVADLFMVFGGFTTTLYTSLHGYFVFGPTGCNLEGFFATLGGEIALWSLVVLAIERYVVVCKPMSNFRFGENHAIMGVAFTWVMALACAAPPLVGWSRYIPEGMQCSCGIDYYTPHEETNNESFVIYMFVVHFIIPLIVIFFCYGQLVFTVKEAAAQQQESATTQKAEKEVTRMVIIMVIAFLICWLPYAGVAFYIFTHQGSDFGPIFMTIPAFFAKTSAVYNPVIYIMMNKQFRNCMVTTLCCGKNPLGDDEASTTVSKTETSQVAPA';
// 3SN6: Endolysin,Beta-2 adrenergic
const seqB = 'DYKDDDDAENLYFQGNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNTNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRAALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYAADEVWVVGMGIVMSLIVLAIVFGNVLVITAIAKFERLQTVTNYFITSLACADLVMGLAVVPFGAAHILTKTWTFGNFWCEFWTSIDVLCVTASIETLCVIAVDRYFAITSPFKYQSLLTKNKARVIILMVWIVSGLTSFLPIQMHWYRATHQEAINCYAEETCCDFFTNQAYAIASSIVSFYVPLVIMVFVYSRVFQEAKRQLQKIDKSEGRFHVQNLSQVEQDGRTGHGLRRSSKFCLKEHKALKTLGIIMGTFTLCWLPFFIVNIVHVIQDNLIRKEVYILLNWIGYVNSGFNPLIYCRSPDFRIAFQELLCLRRSSLKAYGNGYSSNGNTGEQSG';
const { aliA, aliB, score } = align(seqA, seqB, {
gapPenalty: -11,
gapExtensionPenalty: -1,
substMatrix: 'blosum62'
});
expect(aliA).toEqual('------------------------------------------------------------------------------------------------------------------------------------------------MNGTEGPNFYVPFSNKTGVVRSPFEA---PQYYLAEPWQFSM--LAAYMFLLIMLGFPINFLTLYVTVQHKKLRTPLNYILLNLAVADLFMVFGGFTTTLYTSLH---GYFVFGPTGCNLEGFFATLGGEIALWSLVVLAIERYVVVCKPMS-NFRFGENHAIMGVAFTWVMA-LACAAPPLVGWSRYI-PEGMQC----SCGIDYYTPHEETNNESFVIYMFVVHFIIPLIVIFFCYGQLV----------------FTVKEAAAQQQESATTQ----------KAEKEVTRMVIIMVIAFLICWLPYAGVAFYIFTHQGSDFGPIFMTIPAFFAKTSAVYNPVIYIMMNKQFRNCMVTTLCCGKNPLGDDEASTTVSKTETSQVAPA');
expect(aliB).toEqual('DYKDDDDAENLYFQGNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNTNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRAALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKS-RWYNQTPNRAKRVITTFRTGTWDAYAADEVWVVGMGIVMSLIVLAIVFG---NVLVITAIAKFERLQTVTNYFITSLACADLVM---GLAVVPFGAAHILTKTWTFGNFWCEFWTSIDVLCVTASIETLCVIAVDRYFAITSPFKYQSLLTKNKARVIILMVWIVSGLTSFLPIQMHWYRATHQEAINCYAEETC-CDFFT------NQAYAIASSIVSFYVPLVIMVFVYSRVFQEAKRQLQKIDKSEGRFHVQNLSQVEQDGRTGHGLRRSSKFCLKEHKALKTLGIIMG-TFTLCWLPFF-IVNIVHVIQDNLIRKEVYILLNWIGYVNSGFNPLIY-CRSPDFRIAFQELLCLRRSSL--KAYGNGYSSNGNTGEQSG');
expect(score).toEqual(118);
});
});

View File

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { SubstitutionMatrix, SubstitutionMatrices, SubstitutionMatrixData } from './substitution-matrix';
const DefaultAlignmentOptions = {
gapPenalty: -11,
gapExtensionPenalty: -1,
substMatrix: 'default' as SubstitutionMatrix | 'default'
};
export type AlignmentOptions = typeof DefaultAlignmentOptions;
export function align(seqA: ArrayLike<string>, seqB: ArrayLike<string>, options: Partial<AlignmentOptions> = {}) {
const o = { ...DefaultAlignmentOptions, ...options };
const alignment = new Alignment(seqA, seqB, o);
alignment.calculate();
return alignment.trace();
}
class Alignment {
readonly gapPenalty: number; readonly gapExtensionPenalty: number
readonly substMatrix: SubstitutionMatrixData | undefined
readonly n: number; readonly m: number
readonly S: number[][] = []; readonly V: number[][] = []; readonly H: number[][] = []
constructor (readonly seqA: ArrayLike<string>, readonly seqB: ArrayLike<string>, options: AlignmentOptions) {
this.gapPenalty = options.gapPenalty;
this.gapExtensionPenalty = options.gapExtensionPenalty;
this.substMatrix = options.substMatrix === 'default' ? undefined : SubstitutionMatrices[options.substMatrix];
this.n = this.seqA.length;
this.m = this.seqB.length;
}
private initMatrices () {
const { n, m, gapPenalty, S, V, H } = this;
for (let i = 0; i <= n; ++i) {
S[i] = [], V[i] = [], H[i] = [];
for (let j = 0; j <= m; ++j) {
S[i][j] = 0, V[i][j] = 0, H[i][j] = 0;
}
}
for (let i = 0; i <= n; ++i) {
S[i][0] = gapPenalty;
H[i][0] = -Infinity;
}
for (let j = 0; j <= m; ++j) {
S[0][j] = gapPenalty;
V[0][j] = -Infinity;
}
S[0][0] = 0;
}
private makeScoreFn () {
const { seqA, seqB, substMatrix } = this;
if (substMatrix) {
return function score (i: number, j: number) {
const cA = seqA[i];
const cB = seqB[j];
return substMatrix[cA]?.[cB] ?? -4;
};
} else {
return function scoreNoSubstMat (i: number, j: number) {
const cA = seqA[i];
const cB = seqB[j];
return cA === cB ? 5 : -3;
};
}
}
calculate () {
this.initMatrices();
const scoreFn = this.makeScoreFn();
const { V, H, S, n, m, gapExtensionPenalty, gapPenalty } = this;
let Vi1, Si1, Vi, Hi, Si;
for (let i = 1; i <= n; ++i) {
Si1 = S[i - 1], Vi1 = V[i - 1];
Vi = V[i], Hi = H[i], Si = S[i];
for (let j = 1; j <= m; ++j) {
Vi[j] = Math.max(
Si1[j] + gapPenalty,
Vi1[j] + gapExtensionPenalty
);
Hi[j] = Math.max(
Si[j - 1] + gapPenalty,
Hi[j - 1] + gapExtensionPenalty
);
Si[j] = Math.max(
Si1[j - 1] + scoreFn(i - 1, j - 1), // match
Vi[j], // del
Hi[j] // ins
);
}
}
}
trace (): { aliA: ArrayLike<string>, aliB: ArrayLike<string>, score: number } {
const scoreFn = this.makeScoreFn();
const { V, H, S, seqA, seqB, gapExtensionPenalty, gapPenalty } = this;
let i = this.n;
let j = this.m;
let mat: 'S' | 'V' | 'H';
let score: number;
let aliA = '';
let aliB = '';
if (S[i][j] >= V[i][j]) {
mat = 'S';
score = S[i][j];
} else if (V[i][j] >= H[i][j]) {
mat = 'V';
score = V[i][j];
} else {
mat = 'H';
score = H[i][j];
}
while (i > 0 && j > 0) {
if (mat === 'S') {
if (S[i][j] === S[i - 1][j - 1] + scoreFn(i - 1, j - 1)) {
aliA = seqA[i - 1] + aliA;
aliB = seqB[j - 1] + aliB;
--i;
--j;
mat = 'S';
} else if (S[i][j] === V[i][j]) {
mat = 'V';
} else if (S[i][j] === H[i][j]) {
mat = 'H';
} else {
--i;
--j;
}
} else if (mat === 'V') {
if (V[i][j] === V[i - 1][j] + gapExtensionPenalty) {
aliA = seqA[i - 1] + aliA;
aliB = '-' + aliB;
--i;
mat = 'V';
} else if (V[i][j] === S[i - 1][j] + gapPenalty) {
aliA = seqA[i - 1] + aliA;
aliB = '-' + aliB;
--i;
mat = 'S';
} else {
--i;
}
} else if (mat === 'H') {
if (H[i][j] === H[i][j - 1] + gapExtensionPenalty) {
aliA = '-' + aliA;
aliB = seqB[j - 1] + aliB;
--j;
mat = 'H';
} else if (H[i][j] === S[i][j - 1] + gapPenalty) {
aliA = '-' + aliA;
aliB = seqB[j - 1] + aliB;
--j;
mat = 'S';
} else {
--j;
}
}
}
while (i > 0) {
aliA = seqA[i - 1] + aliA;
aliB = '-' + aliB;
--i;
}
while (j > 0) {
aliA = '-' + aliA;
aliB = seqB[j - 1] + aliB;
--j;
}
return { aliA, aliB, score };
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StructureElement, Unit } from '../../structure/structure';
import { AlignmentOptions, align } from './alignment';
import { OrderedSet } from '../../../mol-data/int';
export { AlignSequences };
namespace AlignSequences {
export type Input = {
a: StructureElement.Loci.Element,
b: StructureElement.Loci.Element
}
/** `a` and `b` contain matching pairs, i.e. `a.indices[0]` aligns with `b.indices[0]` */
export type Result = {
a: StructureElement.Loci.Element,
b: StructureElement.Loci.Element,
score: number
}
function createSeqIdIndicesMap(element: StructureElement.Loci.Element) {
const seqIds = new Map<number, StructureElement.UnitIndex[]>();
if (Unit.isAtomic(element.unit)) {
const { label_seq_id } = element.unit.model.atomicHierarchy.residues;
const { residueIndex } = element.unit;
for (let i = 0, il = OrderedSet.size(element.indices); i < il; ++i) {
const uI = OrderedSet.getAt(element.indices, i);
const eI = element.unit.elements[uI];
const seqId = label_seq_id.value(residueIndex[eI]);
if (seqIds.has(seqId)) seqIds.get(seqId)!.push(uI);
else seqIds.set(seqId, [uI]);
}
} else if (Unit.isCoarse(element.unit)) {
const { seq_id_begin } = Unit.isSpheres(element.unit)
? element.unit.model.coarseHierarchy.spheres
: element.unit.model.coarseHierarchy.gaussians;
for (let i = 0, il = OrderedSet.size(element.indices); i < il; ++i) {
const uI = OrderedSet.getAt(element.indices, i);
const eI = element.unit.elements[uI];
const seqId = seq_id_begin.value(eI);
seqIds.set(seqId, [uI]);
}
}
return seqIds;
}
export function compute(input: Input, options: Partial<AlignmentOptions> = {}): Result {
const seqA = getSequence(input.a.unit);
const seqB = getSequence(input.b.unit);
const seqIdIndicesA = createSeqIdIndicesMap(input.a);
const seqIdIndicesB = createSeqIdIndicesMap(input.b);
const indicesA: StructureElement.UnitIndex[] = [];
const indicesB: StructureElement.UnitIndex[] = [];
const { aliA, aliB, score } = align(seqA.code.toArray(), seqB.code.toArray(), options);
let seqIdxA = 0, seqIdxB = 0;
for (let i = 0, il = aliA.length; i < il; ++i) {
if (aliA[i] === '-' || aliB[i] === '-') {
if (aliA[i] !== '-') seqIdxA += 1;
if (aliB[i] !== '-') seqIdxB += 1;
continue;
}
const seqIdA = seqA.seqId.value(seqIdxA);
const seqIdB = seqB.seqId.value(seqIdxB);
if (seqIdIndicesA.has(seqIdA) && seqIdIndicesB.has(seqIdB)) {
const iA = seqIdIndicesA.get(seqIdA)!;
const iB = seqIdIndicesB.get(seqIdB)!;
// use min length to guard against alternate locations
for (let j = 0, jl = Math.min(iA.length, iB.length); j < jl; ++j) {
indicesA.push(iA[j]);
indicesB.push(iB[j]);
}
}
seqIdxA += 1, seqIdxB += 1;
}
const outA = OrderedSet.intersect(OrderedSet.ofSortedArray(indicesA), input.a.indices);
const outB = OrderedSet.intersect(OrderedSet.ofSortedArray(indicesB), input.b.indices);
return {
a: { unit: input.a.unit, indices: outA },
b: { unit: input.b.unit, indices: outB },
score
};
}
}
function entityKey(unit: Unit) {
switch (unit.kind) {
case Unit.Kind.Atomic:
return unit.model.atomicHierarchy.index.getEntityFromChain(unit.chainIndex[unit.elements[0]]);
case Unit.Kind.Spheres:
return unit.model.coarseHierarchy.spheres.entityKey[unit.elements[0]];
case Unit.Kind.Gaussians:
return unit.model.coarseHierarchy.gaussians.entityKey[unit.elements[0]];
}
}
function getSequence(unit: Unit) {
return unit.model.sequence.byEntityKey[entityKey(unit)].sequence;
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mutable } from '../../../mol-util/type-helpers';
const aminoacidsX = 'ACDEFGHIKLMNPQRSTVWY';
const aminoacids = 'ARNDCQEGHILKMFPSTWYVBZX';
const blosum62x = [
[4, 0, -2, -1, -2, 0, -2, -1, -1, -1, -1, -2, -1, -1, -1, 1, 0, 0, -3, -2], // A
[0, 9, -3, -4, -2, -3, -3, -1, -3, -1, -1, -3, -3, -3, -3, -1, -1, -1, -2, -2], // C
[-2, -3, 6, 2, -3, -1, -1, -3, -1, -4, -3, 1, -1, 0, -2, 0, -1, -3, -4, -3], // D
[-1, -4, 2, 5, -3, -2, 0, -3, 1, -3, -2, 0, -1, 2, 0, 0, -1, -2, -3, -2], // E
[-2, -2, -3, -3, 6, -3, -1, 0, -3, 0, 0, -3, -4, -3, -3, -2, -2, -1, 1, 3], // F
[0, -3, -1, -2, -3, 6, -2, -4, -2, -4, -3, 0, -2, -2, -2, 0, -2, -3, -2, -3], // G
[-2, -3, -1, 0, -1, -2, 8, -3, -1, -3, -2, 1, -2, 0, 0, -1, -2, -3, -2, 2], // H
[-1, -1, -3, -3, 0, -4, -3, 4, -3, 2, 1, -3, -3, -3, -3, -2, -1, 3, -3, -1], // I
[-1, -3, -1, 1, -3, -2, -1, -3, 5, -2, -1, 0, -1, 1, 2, 0, -1, -2, -3, -2], // K
[-1, -1, -4, -3, 0, -4, -3, 2, -2, 4, 2, -3, -3, -2, -2, -2, -1, 1, -2, -1], // L
[-1, -1, -3, -2, 0, -3, -2, 1, -1, 2, 5, -2, -2, 0, -1, -1, -1, 1, -1, -1], // M
[-2, -3, 1, 0, -3, 0, 1, -3, 0, -3, -2, 6, -2, 0, 0, 1, 0, -3, -4, -2], // N
[-1, -3, -1, -1, -4, -2, -2, -3, -1, -3, -2, -2, 7, -1, -2, -1, -1, -2, -4, -3], // P
[-1, -3, 0, 2, -3, -2, 0, -3, 1, -2, 0, 0, -1, 5, 1, 0, -1, -2, -2, -1], // Q
[-1, -3, -2, 0, -3, -2, 0, -3, 2, -2, -1, 0, -2, 1, 5, -1, -1, -3, -3, -2], // R
[1, -1, 0, 0, -2, 0, -1, -2, 0, -2, -1, 1, -1, 0, -1, 4, 1, -2, -3, -2], // S
[0, -1, -1, -1, -2, -2, -2, -1, -1, -1, -1, 0, -1, -1, -1, 1, 5, 0, -2, -2], // T
[0, -1, -3, -2, -1, -3, -3, 3, -2, 1, 1, -3, -2, -2, -3, -2, 0, 4, -3, -1], // V
[-3, -2, -4, -3, 1, -2, -2, -3, -3, -2, -1, -4, -4, -2, -3, -3, -2, -3, 11, 2], // W
[-2, -2, -3, -2, 3, -3, 2, -1, -2, -1, -1, -2, -3, -1, -2, -2, -2, -1, 2, 7] // Y
];
const blosum62 = [
// A R N D C Q E G H I L K M F P S T W Y V B Z X
[4, -1, -2, -2, 0, -1, -1, 0, -2, -1, -1, -1, -1, -2, -1, 1, 0, -3, -2, 0, -2, -1, 0], // A
[-1, 5, 0, -2, -3, 1, 0, -2, 0, -3, -2, 2, -1, -3, -2, -1, -1, -3, -2, -3, -1, 0, -1], // R
[-2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3, 0, -2, -3, -2, 1, 0, -4, -2, -3, 3, 0, -1], // N
[-2, -2, 1, 6, -3, 0, 2, -1, -1, -3, -4, -1, -3, -3, -1, 0, -1, -4, -3, -3, 4, 1, -1], // D
[0, -3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1, -1, -2, -2, -1, -3, -3, -2], // C
[-1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2, 1, 0, -3, -1, 0, -1, -2, -1, -2, 0, 3, -1], // Q
[-1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3, 1, -2, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1], // E
[0, -2, 0, -1, -3, -2, -2, 6, -2, -4, -4, -2, -3, -3, -2, 0, -2, -2, -3, -3, -1, -2, -1], // G
[-2, 0, 1, -1, -3, 0, 0, -2, 8, -3, -3, -1, -2, -1, -2, -1, -2, -2, 2, -3, 0, 0, -1], // H
[-1, -3, -3, -3, -1, -3, -3, -4, -3, 4, 2, -3, 1, 0, -3, -2, -1, -3, -1, 3, -3, -3, -1], // I
[-1, -2, -3, -4, -1, -2, -3, -4, -3, 2, 4, -2, 2, 0, -3, -2, -1, -2, -1, 1, -4, -3, -1], // L
[-1, 2, 0, -1, -3, 1, 1, -2, -1, -3, -2, 5, -1, -3, -1, 0, -1, -3, -2, -2, 0, 1, -1], // K
[-1, -1, -2, -3, -1, 0, -2, -3, -2, 1, 2, -1, 5, 0, -2, -1, -1, -1, -1, 1, -3, -1, -1], // M
[-2, -3, -3, -3, -2, -3, -3, -3, -1, 0, 0, -3, 0, 6, -4, -2, -2, 1, 3, -1, -3, -3, -1], // F
[-1, -2, -2, -1, -3, -1, -1, -2, -2, -3, -3, -1, -2, -4, 7, -1, -1, -4, -3, -2, -2, -1, -2], // P
[1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2, 0, -1, -2, -1, 4, 1, -3, -2, -2, 0, 0, 0], // S
[0, -1, 0, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, -2, -1, 1, 5, -2, -2, 0, -1, -1, 0], // T
[-3, -3, -4, -4, -2, -2, -3, -2, -2, -3, -2, -3, -1, 1, -4, -3, -2, 11, 2, -3, -4, -3, -2], // W
[-2, -2, -2, -3, -2, -1, -2, -3, 2, -1, -1, -2, -1, 3, -3, -2, -2, 2, 7, -1, -3, -2, -1], // Y
[0, -3, -3, -3, -1, -2, -2, -3, -3, 3, 1, -2, 1, -1, -2, -2, 0, -3, -1, 4, -3, -2, -1], // V
[-2, -1, 3, 4, -3, 0, 1, -1, 0, -3, -4, 0, -3, -3, -2, 0, -1, -4, -3, -3, 4, 1, -1], // B
[-1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3, 1, -1, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1], // Z
[0, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 0, 0, -2, -1, -1, -1, -1, -1] // X
];
export type SubstitutionMatrixData = Readonly<{ [k: string]: Readonly<{ [k: string]: number }> }>;
function prepareMatrix (cellNames: string, mat: number[][]): SubstitutionMatrixData {
let j: number;
let i = 0;
const matDict: Mutable<SubstitutionMatrixData> = {};
mat.forEach(row => {
j = 0;
const rowDict: { [k: string]: number } = {};
row.forEach(elm => rowDict[cellNames[j++]] = elm);
matDict[cellNames[i++]] = rowDict;
});
return matDict;
}
export const SubstitutionMatrices = (() => ({
blosum62: prepareMatrix(aminoacids, blosum62),
blosum62x: prepareMatrix(aminoacidsX, blosum62x)
}))();
export type SubstitutionMatrix = keyof typeof SubstitutionMatrices;

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>
@@ -9,7 +9,6 @@ import { AminoAlphabet, NuclecicAlphabet, getProteinOneLetterCode, getRnaOneLett
import { Column } from '../../mol-data/db';
// TODO add mapping support to other sequence spaces, e.g. uniprot
// TODO sequence alignment (take NGL code as starting point)
type Sequence = Sequence.Protein | Sequence.DNA | Sequence.RNA | Sequence.Generic
@@ -24,14 +23,18 @@ namespace Sequence {
export interface Base<K extends Kind, Alphabet extends string> {
readonly kind: K,
readonly length: number,
readonly offset: number,
/** One letter code */
readonly code: Column<Alphabet>
readonly label: Column<string>
readonly seqId: Column<number>
/** Component id */
readonly compId: Column<string>
/** returns index for given seqId */
readonly index: (seqId: number) => number
/** maps seqId to list of compIds */
readonly microHet: ReadonlyMap<number, string[]>
}
@@ -41,11 +44,6 @@ namespace Sequence {
export interface DNA extends Base<Kind.DNA, NuclecicAlphabet> { }
export interface Generic extends Base<Kind.Generic, 'X' | '-'> { }
export function create<K extends Kind, Alphabet extends string>(kind: K, code: Column<Alphabet>, label: Column<string>, seqId: Column<number>, compId: Column<string>, microHet: Map<number, string[]>, offset: number = 0): Base<K, Alphabet> {
const length = code.rowCount;
return { kind, code, label, seqId, compId, microHet, offset, length };
}
export function getSequenceString(seq: Sequence) {
const array = seq.code.toArray();
return (array instanceof Array ? array : Array.from(array)).join('');
@@ -88,100 +86,60 @@ namespace Sequence {
}
class ResidueNamesImpl<K extends Kind, Alphabet extends string> implements Base<K, Alphabet> {
private _offset = 0;
private _length = 0;
private _microHet: ReadonlyMap<number, string[]> | undefined = void 0;
private _code: Column<Alphabet> | undefined = undefined
private _label: Column<string> | undefined = undefined
public length: number
public code: Column<Alphabet>
public label: Column<string>
public seqId: Column<number>
public compId: Column<string>
public microHet: ReadonlyMap<number, string[]> = new Map()
private codeFromName: (name: string) => string
get code(): Column<Alphabet> {
if (this._code !== void 0) return this._code;
this.create();
return this._code!;
private indexMap: Map<number, number>
index(seqId: number) {
return this.indexMap.get(seqId)!;
}
get label(): Column<string> {
if (this._label !== void 0) return this._label;
this.create();
return this._label!;
}
constructor(public kind: K, compId: Column<string>, seqId: Column<number>) {
const codeFromName = codeProvider(kind);
const codes: string[] = [];
const compIds: string[] = [];
const seqIds: number[] = [];
const microHet = new Map<number, string[]>();
get offset() {
if (this._code !== void 0) return this._offset;
this.create();
return this._offset;
}
let idx = 0;
const indexMap = new Map<number, number>();
for (let i = 0, il = seqId.rowCount; i < il; ++i) {
const seq_id = seqId.value(i);
get length() {
if (this._code !== void 0) return this._length;
this.create();
return this._length;
}
get microHet(): ReadonlyMap<number, string[]> {
if (this._microHet !== void 0) return this._microHet;
this.create();
return this._microHet!;
}
private create() {
let maxSeqId = 0, minSeqId = Number.MAX_SAFE_INTEGER;
for (let i = 0, _i = this.seqId.rowCount; i < _i; i++) {
const id = this.seqId.value(i);
if (maxSeqId < id) maxSeqId = id;
if (id < minSeqId) minSeqId = id;
}
const count = maxSeqId - minSeqId + 1;
const sequenceArray = new Array<string>(maxSeqId + 1);
const labels = new Array<string[]>(maxSeqId + 1);
for (let i = 0; i < count; i++) {
sequenceArray[i] = '-';
labels[i] = [];
}
const compIds = new Array<string[]>(maxSeqId + 1);
for (let i = minSeqId; i <= maxSeqId; ++i) {
compIds[i] = [];
}
for (let i = 0, _i = this.seqId.rowCount; i < _i; i++) {
const seqId = this.seqId.value(i);
const idx = seqId - minSeqId;
const name = this.compId.value(i);
const code = this.codeFromName(name);
// in case of MICROHETEROGENEITY `sequenceArray[idx]` may already be set
if (!sequenceArray[idx] || sequenceArray[idx] === '-') {
sequenceArray[idx] = code;
if (!indexMap.has(seq_id)) {
indexMap.set(seq_id, idx);
const comp_id = compId.value(i);
compIds[idx] = comp_id;
seqIds[idx] = seq_id;
codes[idx] = codeFromName(comp_id);
idx += 1;
} else {
// micro-heterogeneity
if (!microHet.has(seq_id)) {
microHet.set(seq_id, [compIds[indexMap.get(seq_id)!], compId.value(i)]);
} else {
microHet.get(seq_id)!.push(compId.value(i));
}
}
labels[idx].push(code === 'X' ? name : code);
compIds[seqId].push(name);
}
const microHet = new Map();
for (let i = minSeqId; i <= maxSeqId; ++i) {
if (compIds[i].length > 1) microHet.set(i, compIds[i]);
const labels: string[] = [];
for (let i = 0, il = idx; i < il; ++i) {
const mh = microHet.get(seqIds[i]);
labels[i] = mh ? `(${mh.join('|')})` : codes[i];
}
this._code = Column.ofStringArray(sequenceArray) as Column<Alphabet>;
this._label = Column.ofLambda({
value: i => {
const l = labels[i];
return l.length > 1 ? `(${l.join('|')})` : l.join('');
},
rowCount: labels.length,
schema: Column.Schema.str
});
this._microHet = microHet;
this._offset = minSeqId - 1;
this._length = count;
}
constructor(public kind: K, public compId: Column<string>, public seqId: Column<number>) {
this.codeFromName = codeProvider(kind);
this.length = idx;
this.code = Column.ofStringArray(codes) as Column<Alphabet>;
this.compId = Column.ofStringArray(compIds);
this.seqId = Column.ofIntArray(seqIds);
this.label = Column.ofStringArray(labels);
this.microHet = microHet;
this.indexMap = indexMap;
}
}
@@ -192,13 +150,17 @@ namespace Sequence {
}
class SequenceRangesImpl<K extends Kind, Alphabet extends string> implements Base<K, Alphabet> {
public offset: number
public length: number
public code: Column<Alphabet>
public label: Column<string>
public seqId: Column<number>
public compId: Column<string>
public microHet: ReadonlyMap<number, string[]>
public microHet: ReadonlyMap<number, string[]> = new Map()
private minSeqId: number
index(seqId: number) {
return seqId - this.minSeqId;
}
constructor(public kind: K, private seqIdStart: Column<number>, private seqIdEnd: Column<number>) {
let maxSeqId = 0, minSeqId = Number.MAX_SAFE_INTEGER;
@@ -220,8 +182,8 @@ namespace Sequence {
});
this.compId = Column.ofConst('', count, Column.Schema.str);
this.offset = minSeqId - 1;
this.length = count;
this.minSeqId = minSeqId;
}
}
}

View File

@@ -9,5 +9,4 @@ export * from './structure/coordinates';
export * from './structure/topology';
export * from './structure/model';
export * from './structure/structure';
export * from './structure/query';
export * from './structure/common/custom-property';
export * from './structure/query';

View File

@@ -15,7 +15,7 @@ import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './
import { Model } from '../model';
import { getUniqueEntityIndicesFromStructures, copy_mmCif_category, copy_source_mmCifCategory } from './categories/utils';
import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
import { CustomPropertyDescriptor } from '../common/custom-property';
import { CustomPropertyDescriptor } from '../../custom-property';
import { atom_site_operator_mapping } from './categories/atom_site_operator_mapping';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';

View File

@@ -10,9 +10,9 @@ import StructureSequence from './properties/sequence';
import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic';
import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
import { CustomProperties } from '../common/custom-property';
import { CustomProperties } from '../../custom-property';
import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
import { ModelFormat } from '../../../mol-model-formats/structure/format';
import { ModelFormat } from '../../../mol-model-formats/format';
import { calcModelCenter } from './util';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Mutable } from '../../../mol-util/type-helpers';

View File

@@ -340,9 +340,6 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
const interBonds = inputStructure.interUnitBonds;
const builder = new StructureUniqueSubsetBuilder(inputStructure);
// Note: each bond is visited twice so that bond.atom-a and bond.atom-b both get the "swapped values"
const visitedSourceUnits = new Set<number>();
const atomicBond = ctx.atomicBond;
// Process intra unit bonds
@@ -394,7 +391,6 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
// Process inter unit bonds
for (const bondedUnit of interBonds.getConnectedUnits(inputUnitA)) {
if (visitedSourceUnits.has(bondedUnit.unitB.id)) continue;
const currentUnitB = structure.unitMap.get(bondedUnit.unitB.id);
for (const aI of bondedUnit.connectedIndices) {
@@ -422,8 +418,6 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
}
}
}
visitedSourceUnits.add(inputUnitA.id);
}
return builder.getStructure();

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