mirror of
https://github.com/molstar/molstar.git
synced 2026-06-06 22:54:22 +08:00
Compare commits
80 Commits
v0.7.0-dev
...
v0.7.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6605052db | ||
|
|
8514175da2 | ||
|
|
6a49427fc0 | ||
|
|
7c18e5eb86 | ||
|
|
2a7d258715 | ||
|
|
54fb9beeee | ||
|
|
27ebbc50d5 | ||
|
|
2a1b6e52b2 | ||
|
|
3110e82d92 | ||
|
|
4be999ce32 | ||
|
|
f0d7a4ed2a | ||
|
|
2dacfcb485 | ||
|
|
6218cc5371 | ||
|
|
056ce42097 | ||
|
|
b14b5ca626 | ||
|
|
ffbaa944f2 | ||
|
|
e2ba96174a | ||
|
|
8c5d99bb54 | ||
|
|
b18b3be070 | ||
|
|
2e69b7c419 | ||
|
|
5007f5fb72 | ||
|
|
6fe83a9a70 | ||
|
|
20af084127 | ||
|
|
d6501170e6 | ||
|
|
5f33364514 | ||
|
|
7924c008fa | ||
|
|
2d2a53f28e | ||
|
|
1f7ffabef9 | ||
|
|
16d5c07224 | ||
|
|
2392bfb579 | ||
|
|
b4036f576c | ||
|
|
690d6812dc | ||
|
|
a44aa02f13 | ||
|
|
65ddd6d68a | ||
|
|
754025b3b1 | ||
|
|
f0649c5aa3 | ||
|
|
6df045211c | ||
|
|
8a00540de0 | ||
|
|
0d78905686 | ||
|
|
6edab203c2 | ||
|
|
0abfdb5ee3 | ||
|
|
88369158c9 | ||
|
|
8926575283 | ||
|
|
15b0288ce4 | ||
|
|
28853ec19d | ||
|
|
e3480a076a | ||
|
|
abf6452124 | ||
|
|
2cdd811dd3 | ||
|
|
be6fea39bf | ||
|
|
ed1bc2cd07 | ||
|
|
28d3d5861a | ||
|
|
95d3ef491f | ||
|
|
5c37ddfc6d | ||
|
|
4d9e2d9c91 | ||
|
|
0b1c18913d | ||
|
|
c8c2355d3e | ||
|
|
3d1366024d | ||
|
|
f6964d2a66 | ||
|
|
de60f70af5 | ||
|
|
8f2e619162 | ||
|
|
fbcef01c55 | ||
|
|
641e0639d4 | ||
|
|
5048573976 | ||
|
|
78e4d8536d | ||
|
|
a01d088205 | ||
|
|
4b1d1a045d | ||
|
|
e2857d00b4 | ||
|
|
a55a71d31a | ||
|
|
acf793f112 | ||
|
|
2d58ea28ea | ||
|
|
b21de78eb5 | ||
|
|
376d4b4ee1 | ||
|
|
e39304c7cf | ||
|
|
7cba9cda0c | ||
|
|
04c690e8f9 | ||
|
|
170d0fbc9d | ||
|
|
40d632a7b1 | ||
|
|
2e754d23f4 | ||
|
|
5639a4b37c | ||
|
|
8c959f8a60 |
1
.npmignore
Normal file
1
.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
tsconfig.servers.buildinfo
|
||||
2688
package-lock.json
generated
2688
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.7.0-dev.2",
|
||||
"version": "0.7.0-dev.16",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -34,12 +34,12 @@
|
||||
"model-server-watch": "nodemon --watch lib lib/servers/servers/model/server.js",
|
||||
"volume-server-test": "node lib/servers/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/servers/servers/plugin-state/index.js",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
"preversion": "npm run test && npm run build",
|
||||
"postversion": "git push && git push --tags"
|
||||
},
|
||||
"files": [
|
||||
"lib/"
|
||||
"lib/",
|
||||
"build/viewer/"
|
||||
],
|
||||
"bin": {
|
||||
"cif2bcif": "lib/apps/cif2bcif/index.js",
|
||||
@@ -82,51 +82,52 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^1.13.2",
|
||||
"@graphql-codegen/cli": "^1.13.2",
|
||||
"@graphql-codegen/time": "^1.13.2",
|
||||
"@graphql-codegen/typescript": "^1.13.2",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.2",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.13.2",
|
||||
"@graphql-codegen/typescript-operations": "^1.13.2",
|
||||
"@graphql-codegen/add": "^1.13.3",
|
||||
"@graphql-codegen/cli": "^1.13.3",
|
||||
"@graphql-codegen/time": "^1.13.3",
|
||||
"@graphql-codegen/typescript": "^1.13.3",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.3",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.13.3",
|
||||
"@graphql-codegen/typescript-operations": "^1.13.3",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^2.28.0",
|
||||
"@typescript-eslint/parser": "^2.28.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.29.0",
|
||||
"@typescript-eslint/parser": "^2.29.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"cpx2": "^2.0.0",
|
||||
"css-loader": "^3.5.2",
|
||||
"css-loader": "^3.5.3",
|
||||
"eslint": "^6.8.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"graphql": "^15.0.0",
|
||||
"http-server": "^0.12.1",
|
||||
"jest": "^25.3.0",
|
||||
"jest": "^25.4.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"node-sass": "^4.14.0",
|
||||
"raw-loader": "^4.0.1",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"simple-git": "^1.132.0",
|
||||
"style-loader": "^1.1.4",
|
||||
"style-loader": "^1.2.0",
|
||||
"ts-jest": "^25.4.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-version-file-plugin": "^0.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.9.10",
|
||||
"@material-ui/core": "^4.9.11",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/argparse": "^1.0.38",
|
||||
"@types/benchmark": "^1.0.31",
|
||||
"@types/compression": "1.7.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/node": "^13.13.0",
|
||||
"@types/node-fetch": "^2.5.6",
|
||||
"@types/node": "^13.13.2",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.6",
|
||||
"@types/swagger-ui-dist": "3.0.5",
|
||||
|
||||
65
src/apps/viewer/embedded.html
Normal file
65
src/apps/viewer/embedded.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!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', {
|
||||
extensions: [],
|
||||
|
||||
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>
|
||||
@@ -34,10 +34,43 @@
|
||||
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 hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
layoutShowControls: !hideControls,
|
||||
viewportShowExpand: false,
|
||||
});
|
||||
|
||||
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();
|
||||
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>
|
||||
@@ -8,84 +8,110 @@
|
||||
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] || '');
|
||||
}
|
||||
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)
|
||||
};
|
||||
|
||||
const hideControls = getParam('hide-controls', `[^&]+`) === '1';
|
||||
const DefaultViewerOptions = {
|
||||
extensions: ObjectKeys(Extensions),
|
||||
layoutIsExpanded: true,
|
||||
layoutShowControls: true,
|
||||
layoutShowRemoteState: true,
|
||||
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
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
|
||||
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: DefaultPluginSpec.config
|
||||
};
|
||||
|
||||
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);
|
||||
spec.config?.set(PluginConfig.Viewport.ShowExpand, o.viewportShowExpand);
|
||||
spec.config?.set(PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode);
|
||||
spec.config?.set(PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation);
|
||||
spec.config?.set(PluginConfig.State.DefaultServer, o.pluginStateServer);
|
||||
spec.config?.set(PluginConfig.State.CurrentServer, o.pluginStateServer);
|
||||
spec.config?.set(PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer);
|
||||
spec.config?.set(PluginConfig.Download.DefaultPdbProvider, o.pdbProvider);
|
||||
spec.config?.set(PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider);
|
||||
|
||||
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(undefined, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
@@ -96,10 +122,54 @@ 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(undefined, 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(undefined, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'pdb-dev' as const,
|
||||
params: {
|
||||
id: pdbDev,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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' }));
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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) {
|
||||
@@ -407,7 +405,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);
|
||||
}
|
||||
|
||||
@@ -27,61 +27,75 @@ import { Column } from '../../mol-data/db';
|
||||
import { createModels } from '../../mol-model-formats/structure/basic/parser';
|
||||
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`;
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
@@ -133,7 +147,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]));
|
||||
@@ -288,7 +301,7 @@ 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;
|
||||
|
||||
@@ -303,7 +316,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
}
|
||||
|
||||
// model id in case structure is NMR
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, file);
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
|
||||
if (!model) return;
|
||||
|
||||
let structure: Structure;
|
||||
@@ -354,10 +367,11 @@ 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[] = [];
|
||||
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);
|
||||
@@ -444,6 +458,8 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
}
|
||||
|
||||
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 +467,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 +498,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)
|
||||
@@ -497,8 +526,8 @@ const LoadCellPackModelParams = {
|
||||
['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' }),
|
||||
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
|
||||
}, { options: [['id', 'Id'], ['file', 'File']] }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
ingredients : PD.Group({
|
||||
|
||||
@@ -225,6 +225,7 @@ function getSymbolScale(symbol: string) {
|
||||
function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.RotationAxes, size: number, structure: Structure) {
|
||||
const eye = Vec3();
|
||||
const target = Vec3();
|
||||
const dir = Vec3();
|
||||
const up = Vec3();
|
||||
let pair: Mutable<AssemblySymmetry.RotationAxes> | undefined = undefined;
|
||||
|
||||
@@ -246,8 +247,8 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
|
||||
const a5dir = Vec3.sub(Vec3(), a5.end, a5.start);
|
||||
pair = [a5];
|
||||
for (const a of axes.filter(a => a.order === 3)) {
|
||||
let d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
|
||||
if (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1)) {
|
||||
const d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
|
||||
if (!pair[1] && (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1))) {
|
||||
pair[1] = a;
|
||||
break;
|
||||
}
|
||||
@@ -263,8 +264,8 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
|
||||
Vec3.copy(target, aA.end);
|
||||
if (aB) {
|
||||
Vec3.sub(up, aB.end, aB.start);
|
||||
const d = Vec3.dot(eye, up);
|
||||
if (d < 0) Vec3.negate(up, up);
|
||||
Vec3.sub(dir, eye, target);
|
||||
if (Vec3.dot(dir, up) < 0) Vec3.negate(up, up);
|
||||
Mat4.targetTo(t, eye, target, up);
|
||||
Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol));
|
||||
} else {
|
||||
|
||||
@@ -71,8 +71,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
|
||||
}
|
||||
|
||||
unregister() {
|
||||
// TODO
|
||||
// DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
|
||||
DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
|
||||
|
||||
this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
|
||||
|
||||
@@ -341,7 +340,7 @@ export const ValidationReportDensityFitPreset = StructureRepresentationPresetPro
|
||||
description: 'Color structure based on density fit. Data from wwPDB Validation Report, obtained via RCSB PDB.'
|
||||
},
|
||||
isApplicable(a) {
|
||||
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0]);
|
||||
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromXray(a.data.models[0]) && Model.probablyHasDensityMap(a.data.models[0]);
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
|
||||
@@ -67,7 +67,7 @@ export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationRep
|
||||
factory: DensityFitColorTheme,
|
||||
getParams: () => ({}),
|
||||
defaultValues: PD.getDefaultValues({}),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.hasXrayMap(ctx.structure.models[0]),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.isFromXray(ctx.structure.models[0]) && Model.probablyHasDensityMap(ctx.structure.models[0]),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
|
||||
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)
|
||||
|
||||
@@ -132,12 +132,12 @@ namespace Canvas3D {
|
||||
if (webgl.isContextLost) return;
|
||||
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return;
|
||||
|
||||
console.log('lose context');
|
||||
if (isDebugMode) console.log('lose context');
|
||||
loseContextExt.loseContext();
|
||||
|
||||
setTimeout(() => {
|
||||
if (!webgl.isContextLost) return;
|
||||
console.log('restore context');
|
||||
if (isDebugMode) console.log('restore context');
|
||||
loseContextExt.restoreContext();
|
||||
}, 1000);
|
||||
}, false);
|
||||
|
||||
@@ -159,5 +159,5 @@ const instanceMaterialId = getNextMaterialId();
|
||||
|
||||
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
|
||||
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform);
|
||||
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, materialId);
|
||||
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false, writeDepth: false }, materialId);
|
||||
}
|
||||
@@ -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' })
|
||||
|
||||
@@ -72,15 +72,18 @@ export namespace BaseGeometry {
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
const opaque = props.alpha === undefined ? true : props.alpha === 1;
|
||||
return {
|
||||
visible: true,
|
||||
alphaFactor: 1,
|
||||
pickable: true,
|
||||
opaque: props.alpha === undefined ? true : props.alpha === 1
|
||||
opaque,
|
||||
writeDepth: opaque,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
state.opaque = props.alpha * state.alphaFactor >= 1;
|
||||
state.writeDepth = state.opaque;
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,7 @@ namespace Image {
|
||||
return image;
|
||||
}
|
||||
|
||||
function update(imageTexture: TextureImage<Uint8Array | Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
|
||||
|
||||
function update(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
|
||||
const width = imageTexture.width;
|
||||
const height = imageTexture.height;
|
||||
|
||||
@@ -116,7 +115,7 @@ namespace Image {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
interpolation: PD.Select('bspline', PD.objectToOptions(InterpolationTypes), { isEssential: true }),
|
||||
interpolation: PD.Select('bspline', PD.objectToOptions(InterpolationTypes)),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -176,7 +175,7 @@ namespace Image {
|
||||
}
|
||||
|
||||
function updateValues(values: ImageValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.uAlpha, props.alpha);
|
||||
BaseGeometry.updateValues(values, props);
|
||||
ValueCell.updateIfChanged(values.dInterpolation, props.interpolation);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
if (radialSegments === 2) {
|
||||
// add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t))
|
||||
Vec3.copy(normalVector, v);
|
||||
console.log(i, t);
|
||||
Vec3.normalize(normalVector, normalVector);
|
||||
if (t !== 0 || i % 2 === 0) Vec3.negate(normalVector, normalVector);
|
||||
} else {
|
||||
|
||||
@@ -86,7 +86,8 @@ function createPoints() {
|
||||
visible: true,
|
||||
alphaFactor: 1,
|
||||
pickable: true,
|
||||
opaque: true
|
||||
opaque: true,
|
||||
writeDepth: true
|
||||
};
|
||||
|
||||
return createRenderObject('points', values, state, -1);
|
||||
|
||||
@@ -18,6 +18,7 @@ export type RenderableState = {
|
||||
alphaFactor: number
|
||||
pickable: boolean
|
||||
opaque: boolean
|
||||
writeDepth: boolean,
|
||||
}
|
||||
|
||||
export interface Renderable<T extends RenderableValues> {
|
||||
@@ -43,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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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> }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface TextureImage<T extends Uint8Array | Float32Array> {
|
||||
readonly array: T
|
||||
readonly width: number
|
||||
readonly height: number
|
||||
readonly flipY?: boolean
|
||||
}
|
||||
|
||||
export interface TextureVolume<T extends Uint8Array | Float32Array> {
|
||||
|
||||
@@ -173,47 +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);
|
||||
}
|
||||
|
||||
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) => {
|
||||
@@ -245,11 +251,11 @@ namespace Renderer {
|
||||
|
||||
state.disable(gl.SCISSOR_TEST);
|
||||
state.disable(gl.BLEND);
|
||||
state.depthMask(true);
|
||||
state.colorMask(true, true, true, true);
|
||||
state.enable(gl.DEPTH_TEST);
|
||||
|
||||
if (clear) {
|
||||
state.depthMask(true);
|
||||
if (variant === 'color') {
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1);
|
||||
} else {
|
||||
@@ -268,10 +274,11 @@ namespace Renderer {
|
||||
state.enable(gl.BLEND);
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (!r.state.opaque) {
|
||||
state.depthMask(false);
|
||||
renderObject(r, variant);
|
||||
}
|
||||
if (!r.state.opaque && r.state.writeDepth) renderObject(r, variant);
|
||||
}
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (!r.state.opaque && !r.state.writeDepth) renderObject(r, variant);
|
||||
}
|
||||
} else { // picking & depth
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
|
||||
@@ -50,9 +50,11 @@ function renderableSort(a: Renderable<RenderableValues & BaseValues>, b: Rendera
|
||||
const materialIdB = b.materialId;
|
||||
|
||||
if (drawProgramIdA !== drawProgramIdB) {
|
||||
return drawProgramIdA - drawProgramIdB; // sort by program id to minimize gl state changes
|
||||
// sort by program id to minimize gl state changes
|
||||
return drawProgramIdA - drawProgramIdB;
|
||||
} else if (materialIdA !== materialIdB) {
|
||||
return materialIdA - materialIdB; // sort by material id to minimize gl state changes
|
||||
// sort by material id to minimize gl state changes
|
||||
return materialIdA - materialIdB;
|
||||
} else {
|
||||
return a.id - b.id;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
export default `
|
||||
float depth = length(vViewPosition);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
if (uTransparentBackground == 0) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
if (gl_FragColor.a < 1.0)
|
||||
gl_FragColor.a = fogAlpha;
|
||||
} else {
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,7 +21,6 @@ uniform vec3 uFogColor;
|
||||
|
||||
uniform float uAlpha;
|
||||
uniform float uPickingAlphaThreshold;
|
||||
uniform int uPickable;
|
||||
uniform int uTransparentBackground;
|
||||
|
||||
uniform float uInteriorDarkening;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -35,20 +35,20 @@ varying float vInstance;
|
||||
const float C = 0.333;
|
||||
#endif
|
||||
|
||||
float cubicFilter( float x ){
|
||||
float cubicFilter(float x){
|
||||
float f = x;
|
||||
if( f < 0.0 ){
|
||||
if (f < 0.0) {
|
||||
f = -f;
|
||||
}
|
||||
if( f < 1.0 ){
|
||||
return ( ( 12.0 - 9.0 * B - 6.0 * C ) * ( f * f * f ) +
|
||||
( -18.0 + 12.0 * B + 6.0 *C ) * ( f * f ) +
|
||||
( 6.0 - 2.0 * B ) ) / 6.0;
|
||||
}else if( f >= 1.0 && f < 2.0 ){
|
||||
return ( ( -B - 6.0 * C ) * ( f * f * f )
|
||||
+ ( 6.0 * B + 30.0 * C ) * ( f *f ) +
|
||||
( - ( 12.0 * B ) - 48.0 * C ) * f +
|
||||
8.0 * B + 24.0 * C ) / 6.0;
|
||||
if (f < 1.0) {
|
||||
return ((12.0 - 9.0 * B - 6.0 * C) * (f * f * f) +
|
||||
(-18.0 + 12.0 * B + 6.0 * C) * (f * f) +
|
||||
(6.0 - 2.0 * B)) / 6.0;
|
||||
}else if (f >= 1.0 && f < 2.0){
|
||||
return ((-B - 6.0 * C) * ( f * f * f)
|
||||
+ (6.0 * B + 30.0 * C) * (f * f) +
|
||||
(-(12.0 * B) - 48.0 * C) * f +
|
||||
8.0 * B + 24.0 * C) / 6.0;
|
||||
}else{
|
||||
return 0.0;
|
||||
}
|
||||
@@ -92,25 +92,23 @@ void main() {
|
||||
#else
|
||||
vec4 imageData = texture2D(tImageTex, vUv);
|
||||
#endif
|
||||
imageData.a = clamp(imageData.a, 0.0, 1.0);
|
||||
if (imageData.a > 0.9) imageData.a = 1.0;
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
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.01)
|
||||
if (imageData.a < 0.05)
|
||||
discard;
|
||||
|
||||
#ifdef enabledFragDepth
|
||||
@@ -119,7 +117,7 @@ void main() {
|
||||
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_color)
|
||||
if (imageData.a < 0.01)
|
||||
if (imageData.a < 0.05)
|
||||
discard;
|
||||
|
||||
gl_FragColor = imageData;
|
||||
|
||||
@@ -118,6 +118,14 @@ export function getAttachment(gl: GLRenderingContext, extensions: WebGLExtension
|
||||
throw new Error('unknown texture attachment');
|
||||
}
|
||||
|
||||
function isTexture2d(x: TextureImage<any> | TextureVolume<any>, target: number, gl: GLRenderingContext): x is TextureImage<any> {
|
||||
return target === gl.TEXTURE_2D;
|
||||
}
|
||||
|
||||
function isTexture3d(x: TextureImage<any> | TextureVolume<any>, target: number, gl: WebGL2RenderingContext): x is TextureImage<any> {
|
||||
return target === gl.TEXTURE_3D;
|
||||
}
|
||||
|
||||
export interface Texture {
|
||||
readonly id: number
|
||||
readonly target: number
|
||||
@@ -214,14 +222,13 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
|
||||
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
|
||||
if (target === gl.TEXTURE_2D) {
|
||||
const { array, width: _width, height: _height } = data as TextureImage<any>;
|
||||
width = _width, height = _height;
|
||||
gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, array);
|
||||
} else if (isWebGL2(gl) && target === gl.TEXTURE_3D) {
|
||||
const { array, width: _width, height: _height, depth: _depth } = data as TextureVolume<any>;
|
||||
width = _width, height = _height, depth = _depth;
|
||||
gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, array);
|
||||
if (isTexture2d(data, target, gl)) {
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
|
||||
width = data.width, height = data.height;
|
||||
gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, data.array);
|
||||
} else if (isWebGL2(gl) && isTexture3d(data, target, gl)) {
|
||||
width = data.width, height = data.height, depth = data.depth;
|
||||
gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, data.array);
|
||||
} else {
|
||||
throw new Error('unknown texture target');
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SimpleBuffer } from './simple-buffer';
|
||||
const fs = typeof document === 'undefined' ? require('fs') as typeof import('fs') : void 0;
|
||||
|
||||
export interface FileHandle {
|
||||
name: string
|
||||
/**
|
||||
* Asynchronously reads data, returning buffer and number of bytes read
|
||||
*
|
||||
@@ -44,8 +45,9 @@ export interface FileHandle {
|
||||
}
|
||||
|
||||
export namespace FileHandle {
|
||||
export function fromBuffer(buffer: SimpleBuffer): FileHandle {
|
||||
export function fromBuffer(buffer: SimpleBuffer, name: string): FileHandle {
|
||||
return {
|
||||
name,
|
||||
readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, size?: number, byteOffset?: number) => {
|
||||
let bytesRead: number;
|
||||
let outBuffer: SimpleBuffer;
|
||||
@@ -82,9 +84,10 @@ export namespace FileHandle {
|
||||
};
|
||||
}
|
||||
|
||||
export function fromDescriptor(file: number): FileHandle {
|
||||
export function fromDescriptor(file: number, name: string): FileHandle {
|
||||
if (fs === undefined) throw new Error('fs module not available');
|
||||
return {
|
||||
name,
|
||||
readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number) => {
|
||||
return new Promise((res, rej) => {
|
||||
let outBuffer: SimpleBuffer;
|
||||
|
||||
@@ -31,7 +31,7 @@ function createCcp4Data() {
|
||||
describe('ccp4 reader', () => {
|
||||
it('basic', async () => {
|
||||
const data = createCcp4Data();
|
||||
const parsed = await CCP4.parse(data).run();
|
||||
const parsed = await CCP4.parse(data, 'test.ccp4').run();
|
||||
|
||||
if (parsed.isError) {
|
||||
throw new Error(parsed.message);
|
||||
|
||||
@@ -8,10 +8,14 @@ import { parseFloat as fastParseFloat, parseInt as fastParseInt, getNumberType,
|
||||
|
||||
describe('common', () => {
|
||||
it('number-parser fastParseFloat', () => {
|
||||
// ignore suffix numbers in parentheses
|
||||
expect(fastParseFloat('11.0829(23)', 0, 11)).toBe(11.0829);
|
||||
// scientific with no space between consecutive values
|
||||
expect(fastParseFloat('-5.1E-01-6.1E-01', 0, 11)).toBe(-0.51);
|
||||
});
|
||||
|
||||
it('number-parser fastParseInt', () => {
|
||||
// ignore suffix numbers in parentheses
|
||||
expect(fastParseInt('11(23)', 0, 11)).toBe(11);
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
|
||||
const buffer = createTypedArrayBufferContext(count, valueType);
|
||||
readCcp4Slices(header, buffer, file, offset, byteCount, littleEndian);
|
||||
|
||||
const result: Ccp4File = { header, values: buffer.values };
|
||||
const result: Ccp4File = { header, values: buffer.values, name: file.name };
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -174,6 +174,6 @@ export function parseFile(file: FileHandle, size: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export function parse(buffer: Uint8Array) {
|
||||
return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer)), buffer.length);
|
||||
export function parse(buffer: Uint8Array, name: string) {
|
||||
return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer), name), buffer.length);
|
||||
}
|
||||
@@ -114,6 +114,7 @@ export interface Ccp4Header {
|
||||
* CCP4 format does not use the ORIGIN header records (words 50-52)
|
||||
*/
|
||||
export interface Ccp4File {
|
||||
name: string
|
||||
header: Ccp4Header
|
||||
values: Float32Array | Int16Array | Int8Array | Uint16Array
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { parseFloat as fastParseFloat } from '../common/text/number-parser';
|
||||
// https://h5cube-spec.readthedocs.io/en/latest/cubeformat.html
|
||||
|
||||
export interface CubeFile {
|
||||
name: string,
|
||||
header: CubeFile.Header,
|
||||
atoms: CubeFile.Atoms,
|
||||
values: Float64Array
|
||||
@@ -24,6 +25,7 @@ export interface CubeFile {
|
||||
|
||||
export namespace CubeFile {
|
||||
export interface Header {
|
||||
orbitals: boolean,
|
||||
comment1: string,
|
||||
comment2: string,
|
||||
atomCount: number,
|
||||
@@ -79,8 +81,7 @@ function readHeader(tokenizer: Tokenizer) {
|
||||
for (let i = 0, _i = +counts[0]; i < _i; i++) dataSetIds.push(+counts[i + 1]);
|
||||
}
|
||||
|
||||
const header: CubeFile.Header = { comment1, comment2, atomCount, origin, dim: Vec3.create(NVX, NVY, NVZ), basisX, basisY, basisZ, dataSetIds };
|
||||
|
||||
const header: CubeFile.Header = { orbitals: rawAtomCount < 0, comment1, comment2, atomCount, origin, dim: Vec3.create(NVX, NVY, NVZ), basisX, basisY, basisZ, dataSetIds };
|
||||
return { header, atoms };
|
||||
}
|
||||
|
||||
@@ -129,12 +130,12 @@ function readValues(ctx: RuntimeContext, tokenizer: Tokenizer, header: CubeFile.
|
||||
}, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
|
||||
}
|
||||
|
||||
export function parseCube(data: string) {
|
||||
export function parseCube(data: string, name: string) {
|
||||
return Task.create<Result<CubeFile>>('Parse Cube', async taskCtx => {
|
||||
await taskCtx.update('Reading header...');
|
||||
const tokenizer = Tokenizer(data);
|
||||
const { header, atoms } = readHeader(tokenizer);
|
||||
const values = await readValues(taskCtx, tokenizer, header);
|
||||
return Result.success({ header, atoms, values });
|
||||
return Result.success({ header, atoms, values, name });
|
||||
});
|
||||
}
|
||||
@@ -134,7 +134,7 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
|
||||
const values = new Float32Array(valueCount);
|
||||
await parseDsn6Values(header, buffer, values, littleEndian);
|
||||
|
||||
const result: Dsn6File = { header, values };
|
||||
const result: Dsn6File = { header, values, name: file.name };
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,6 @@ export function parseFile(file: FileHandle, size: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export function parse(buffer: Uint8Array) {
|
||||
return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer)), buffer.length);
|
||||
export function parse(buffer: Uint8Array, name: string) {
|
||||
return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer), name), buffer.length);
|
||||
}
|
||||
@@ -39,6 +39,7 @@ export interface Dsn6Header {
|
||||
* BRIX http://svn.cgl.ucsf.edu/svn/chimera/trunk/libs/VolumeData/dsn6/brix-1.html
|
||||
*/
|
||||
export interface Dsn6File {
|
||||
name: string
|
||||
header: Dsn6Header
|
||||
values: Float32Array
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { utf8Read } from '../../common/utf8';
|
||||
// http://apbs-pdb2pqr.readthedocs.io/en/latest/formats/opendx.html
|
||||
|
||||
export interface DxFile {
|
||||
name: string,
|
||||
header: DxFile.Header,
|
||||
values: Float64Array
|
||||
}
|
||||
@@ -88,16 +89,16 @@ function readValuesText(ctx: RuntimeContext, tokenizer: Tokenizer, header: DxFil
|
||||
}, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
|
||||
}
|
||||
|
||||
async function parseText(taskCtx: RuntimeContext, data: string) {
|
||||
async function parseText(taskCtx: RuntimeContext, data: string, name: string) {
|
||||
await taskCtx.update('Reading header...');
|
||||
const tokenizer = Tokenizer(data as string);
|
||||
const { header } = readHeader(tokenizer);
|
||||
await taskCtx.update('Reading values...');
|
||||
const values = await readValuesText(taskCtx, tokenizer, header);
|
||||
return Result.success({ header, values });
|
||||
return Result.success({ header, values, name });
|
||||
}
|
||||
|
||||
async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array) {
|
||||
async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array, name: string) {
|
||||
await taskCtx.update('Reading header...');
|
||||
|
||||
const headerString = utf8Read(data, 0, 1000);
|
||||
@@ -117,12 +118,12 @@ async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array) {
|
||||
|
||||
// TODO: why doesnt this work? throw "attempting to construct out-of-bounds TypedArray"
|
||||
// const values = new Float64Array(data.buffer, data.byteOffset + headerByteCount, header.dim[0] * header.dim[1] * header.dim[2]);
|
||||
return Result.success({ header, values });
|
||||
return Result.success({ header, values, name });
|
||||
}
|
||||
|
||||
export function parseDx(data: string | Uint8Array) {
|
||||
export function parseDx(data: string | Uint8Array, name: string) {
|
||||
return Task.create<Result<DxFile>>('Parse Cube', taskCtx => {
|
||||
if (typeof data === 'string') return parseText(taskCtx, data);
|
||||
return parseBinary(taskCtx, data);
|
||||
if (typeof data === 'string') return parseText(taskCtx, data, name);
|
||||
return parseBinary(taskCtx, data, name);
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
1
src/mol-io/reader/mol2/schema.d.ts
vendored
1
src/mol-io/reader/mol2/schema.d.ts
vendored
@@ -63,5 +63,6 @@ export interface Mol2Structure {
|
||||
}
|
||||
|
||||
export interface Mol2File {
|
||||
name: string
|
||||
structures: Mol2Structure[]
|
||||
}
|
||||
@@ -263,4 +263,59 @@ describe('tensor', () => {
|
||||
|
||||
expect(data).toEqual(exp);
|
||||
});
|
||||
|
||||
it('indexing', () => {
|
||||
function permutations<T>(inputArr: T[]): T[][] {
|
||||
let result: T[][] = [];
|
||||
function permute(arr: any, m: any = []) {
|
||||
if (arr.length === 0) {
|
||||
result.push(m);
|
||||
} else {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let curr = arr.slice();
|
||||
let next = curr.splice(i, 1);
|
||||
permute(curr.slice(), m.concat(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
permute(inputArr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (let dim = 1; dim <= 5; dim++) {
|
||||
const axes = [], dims: number[] = [];
|
||||
const u: number[] = [], v: number[] = [];
|
||||
|
||||
for (let i = 0; i < dim; i++) {
|
||||
axes.push(i);
|
||||
dims.push(3);
|
||||
u.push(0);
|
||||
v.push(0);
|
||||
}
|
||||
|
||||
const forEachDim = (space: T.Space, d: number): boolean => {
|
||||
if (d === dim) {
|
||||
const o = space.dataOffset(...u);
|
||||
space.getCoords(o, v);
|
||||
|
||||
for (let e = 0; e < dims.length; e++) {
|
||||
expect(u[e]).toEqual(v[e]);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < dims[d]; i++) {
|
||||
u[d] = i;
|
||||
if (!forEachDim(space, d + 1)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const ao of permutations(axes)) {
|
||||
const space = T.Space(dims, ao);
|
||||
if (!forEachDim(space, 0)) break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4, Mat3 } from './3d';
|
||||
@@ -21,6 +22,8 @@ export namespace Tensor {
|
||||
get(data: Tensor.Data, ...coords: number[]): number
|
||||
set(data: Tensor.Data, ...coordsAndValue: number[]): number
|
||||
add(data: Tensor.Data, ...coordsAndValue: number[]): number
|
||||
dataOffset(...coords: number[]): number,
|
||||
getCoords(dataOffset: number, coords: { [i: number]: number }): number[]
|
||||
}
|
||||
|
||||
interface Layout {
|
||||
@@ -46,8 +49,8 @@ export namespace Tensor {
|
||||
|
||||
export function Space(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Space {
|
||||
const layout = Layout(dimensions, axisOrderSlowToFast, ctor);
|
||||
const { get, set, add } = accessors(layout);
|
||||
return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set, add };
|
||||
const { get, set, add, dataOffset, getCoords } = accessors(layout);
|
||||
return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set, add, dataOffset, getCoords };
|
||||
}
|
||||
|
||||
export function Data1(values: ArrayLike<number>): Data { return values as Data; }
|
||||
@@ -95,13 +98,15 @@ export namespace Tensor {
|
||||
return true;
|
||||
}
|
||||
|
||||
function accessors(layout: Layout): { get: Space['get'], set: Space['set'], add: Space['add'] } {
|
||||
function accessors(layout: Layout): { get: Space['get'], set: Space['set'], add: Space['add'], dataOffset: Space['dataOffset'], getCoords: Space['getCoords'] } {
|
||||
const { dimensions, axisOrderFastToSlow: ao } = layout;
|
||||
switch (dimensions.length) {
|
||||
case 1: return {
|
||||
get: (t, d) => t[d],
|
||||
set: (t, d, x) => t[d] = x,
|
||||
add: (t, d, x) => t[d] += x
|
||||
add: (t, d, x) => t[d] += x,
|
||||
dataOffset: (d) => d,
|
||||
getCoords: (o, c) => { c[0] = o; return c as number[]; }
|
||||
};
|
||||
case 2: {
|
||||
// column major
|
||||
@@ -110,7 +115,9 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j) => t[j * rows + i],
|
||||
set: (t, i, j, x) => t[j * rows + i] = x,
|
||||
add: (t, i, j, x) => t[j * rows + i] += x
|
||||
add: (t, i, j, x) => t[j * rows + i] += x,
|
||||
dataOffset: (i, j) => j * rows + i,
|
||||
getCoords: (o, c) => { c[0] = o % rows; c[1] = Math.floor(o / rows) ; return c as number[]; }
|
||||
};
|
||||
}
|
||||
if (ao[0] === 1 && ao[1] === 0) {
|
||||
@@ -118,7 +125,9 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j) => t[i * cols + j],
|
||||
set: (t, i, j, x) => t[i * cols + j] = x,
|
||||
add: (t, i, j, x) => t[i * cols + j] += x
|
||||
add: (t, i, j, x) => t[i * cols + j] += x,
|
||||
dataOffset: (i, j) => i * cols + j,
|
||||
getCoords: (o, c) => { c[0] = Math.floor(o / cols); c[1] = o % cols; return c as number[]; }
|
||||
};
|
||||
}
|
||||
throw new Error('bad axis order');
|
||||
@@ -129,7 +138,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[i + j * u + k * uv],
|
||||
set: (t, i, j, k, x ) => t[i + j * u + k * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[i + j * u + k * uv] += x
|
||||
add: (t, i, j, k, x ) => t[i + j * u + k * uv] += x,
|
||||
dataOffset: (i, j, k) => i + j * u + k * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = o % u;
|
||||
c[1] = p % v;
|
||||
c[2] = Math.floor(p / v);
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
if (ao[0] === 0 && ao[1] === 2 && ao[2] === 1) { // 021 ikj
|
||||
@@ -137,7 +154,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[i + k * u + j * uv],
|
||||
set: (t, i, j, k, x ) => t[i + k * u + j * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[i + k * u + j * uv] += x
|
||||
add: (t, i, j, k, x ) => t[i + k * u + j * uv] += x,
|
||||
dataOffset: (i, j, k) => i + k * u + j * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = o % u;
|
||||
c[1] = Math.floor(p / v);
|
||||
c[2] = p % v;
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
if (ao[0] === 1 && ao[1] === 0 && ao[2] === 2) { // 102 jik
|
||||
@@ -145,7 +170,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[j + i * u + k * uv],
|
||||
set: (t, i, j, k, x ) => t[j + i * u + k * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[j + i * u + k * uv] += x
|
||||
add: (t, i, j, k, x ) => t[j + i * u + k * uv] += x,
|
||||
dataOffset: (i, j, k) => j + i * u + k * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = p % v;
|
||||
c[1] = o % u;
|
||||
c[2] = Math.floor(p / v);
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
if (ao[0] === 1 && ao[1] === 2 && ao[2] === 0) { // 120 jki
|
||||
@@ -153,7 +186,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[j + k * u + i * uv],
|
||||
set: (t, i, j, k, x ) => t[j + k * u + i * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[j + k * u + i * uv] += x
|
||||
add: (t, i, j, k, x ) => t[j + k * u + i * uv] += x,
|
||||
dataOffset: (i, j, k) => j + k * u + i * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = Math.floor(p / v);
|
||||
c[1] = o % u;
|
||||
c[2] = p % v;
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
if (ao[0] === 2 && ao[1] === 0 && ao[2] === 1) { // 201 kij
|
||||
@@ -161,7 +202,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[k + i * u + j * uv],
|
||||
set: (t, i, j, k, x ) => t[k + i * u + j * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[k + i * u + j * uv] += x
|
||||
add: (t, i, j, k, x ) => t[k + i * u + j * uv] += x,
|
||||
dataOffset: (i, j, k) => k + i * u + j * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = p % v;
|
||||
c[1] = Math.floor(p / v);
|
||||
c[2] = o % u;
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
if (ao[0] === 2 && ao[1] === 1 && ao[2] === 0) { // 210 kji
|
||||
@@ -169,7 +218,15 @@ export namespace Tensor {
|
||||
return {
|
||||
get: (t, i, j, k) => t[k + j * u + i * uv],
|
||||
set: (t, i, j, k, x ) => t[k + j * u + i * uv] = x,
|
||||
add: (t, i, j, k, x ) => t[k + j * u + i * uv] += x
|
||||
add: (t, i, j, k, x ) => t[k + j * u + i * uv] += x,
|
||||
dataOffset: (i, j, k) => k + j * u + i * uv,
|
||||
getCoords: (o, c) => {
|
||||
const p = Math.floor(o / u);
|
||||
c[0] = Math.floor(p / v);
|
||||
c[1] = p % v;
|
||||
c[2] = o % u;
|
||||
return c as number[];
|
||||
}
|
||||
};
|
||||
}
|
||||
throw new Error('bad axis order');
|
||||
@@ -177,7 +234,9 @@ export namespace Tensor {
|
||||
default: return {
|
||||
get: (t, ...c) => t[dataOffset(layout, c)],
|
||||
set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1],
|
||||
add: (t, ...c) => t[dataOffset(layout, c)] += c[c.length - 1]
|
||||
add: (t, ...c) => t[dataOffset(layout, c)] += c[c.length - 1],
|
||||
dataOffset: (...c) => dataOffset(layout, c),
|
||||
getCoords: (o, c) => getCoords(layout, o, c as number[]),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -199,6 +258,21 @@ export namespace Tensor {
|
||||
return o;
|
||||
}
|
||||
|
||||
function getCoords(layout: Layout, o: number, coords: number[]) {
|
||||
const { dimensions: dim, axisOrderFastToSlow: ao } = layout;
|
||||
const d = dim.length;
|
||||
|
||||
let c = o;
|
||||
for (let i = 0; i < d; i++) {
|
||||
const d = dim[ao[i]];
|
||||
coords[ao[i]] = c % d;
|
||||
c = Math.floor(c / d);
|
||||
}
|
||||
coords[ao[d + 1]] = c;
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
// Convers "slow to fast" axis order to "fast to slow" and vice versa.
|
||||
export function invertAxisOrder(v: number[]) {
|
||||
const ret: number[] = [];
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -15,7 +15,7 @@ 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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { CustomPropertyDescriptor, Model } from '../../../mol-model/structure';
|
||||
import { ModelFormat } from '../format';
|
||||
import { ModelFormat } from '../../format';
|
||||
|
||||
class FormatRegistry<T> {
|
||||
private map = new Map<ModelFormat['kind'], (model: Model) => T | undefined>()
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
98
src/mol-model-formats/structure/mol2.ts
Normal file
98
src/mol-model-formats/structure/mol2.ts
Normal 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));
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ 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';
|
||||
|
||||
/** When available (e.g. in MRC files) use ORIGIN records instead of N[CRS]START */
|
||||
export function getCcp4Origin(header: Ccp4Header): Vec3 {
|
||||
@@ -75,7 +76,24 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
|
||||
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)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { Mat4, Tensor } from '../../mol-math/linear-algebra';
|
||||
import { VolumeData } from '../../mol-model/volume/data';
|
||||
import { Task } from '../../mol-task';
|
||||
import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
|
||||
import { ModelFormat } from '../format';
|
||||
|
||||
export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<VolumeData> {
|
||||
return Task.create<VolumeData>('Create Volume Data', async () => {
|
||||
@@ -51,7 +52,24 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
|
||||
max: arrayMax(values),
|
||||
mean: arrayMean(values),
|
||||
sigma: arrayRms(values)
|
||||
}
|
||||
},
|
||||
sourceData: CubeFormat.create(source)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@ import { VolumeData } from '../../mol-model/volume/data';
|
||||
import { Task } from '../../mol-task';
|
||||
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
|
||||
import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { ModelFormat } from '../format';
|
||||
|
||||
function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
|
||||
export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
|
||||
return Task.create<VolumeData>('Create Volume Data', async ctx => {
|
||||
const { volume_data_3d_info: info, volume_data_3d: values } = source;
|
||||
const cell = SpacegroupCell.create(
|
||||
@@ -41,9 +42,24 @@ function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<
|
||||
max: info.max_sampled.value(0),
|
||||
mean: info.mean_sampled.value(0),
|
||||
sigma: info.sigma_sampled.value(0)
|
||||
}
|
||||
},
|
||||
sourceData: DscifFormat.create(source)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,9 @@ 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';
|
||||
|
||||
function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
|
||||
export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
|
||||
return Task.create<VolumeData>('Create Volume Data', async ctx => {
|
||||
const { header, values } = source;
|
||||
const size = Vec3.create(header.xlen, header.ylen, header.zlen);
|
||||
@@ -40,9 +41,24 @@ function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: s
|
||||
max: arrayMax(values),
|
||||
mean: arrayMean(values),
|
||||
sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
|
||||
}
|
||||
},
|
||||
sourceData: Dsn6Format.create(source)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { Mat4, Tensor } from '../../mol-math/linear-algebra';
|
||||
import { VolumeData } from '../../mol-model/volume/data';
|
||||
import { Task } from '../../mol-task';
|
||||
import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
|
||||
import { ModelFormat } from '../format';
|
||||
|
||||
export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<VolumeData> {
|
||||
return Task.create<VolumeData>('Create Volume Data', async () => {
|
||||
@@ -28,7 +29,24 @@ export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<
|
||||
max: arrayMax(values),
|
||||
mean: arrayMean(values),
|
||||
sigma: arrayRms(values)
|
||||
}
|
||||
},
|
||||
sourceData: DxFormat.create(source)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import { FiniteArray } from '../mol-util/type-helpers';
|
||||
import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
|
||||
import { stringToWords } from '../mol-util/string';
|
||||
import { Volume } from './volume/volume';
|
||||
import { VolumeData } from './volume';
|
||||
|
||||
/** A Loci that includes every loci */
|
||||
export const EveryLoci = { kind: 'every-loci' as 'every-loci' };
|
||||
@@ -162,12 +161,11 @@ namespace Loci {
|
||||
} else if (loci.kind === 'data-loci') {
|
||||
return loci.getBoundingSphere(boundingSphere);
|
||||
} else if (loci.kind === 'volume-loci') {
|
||||
return VolumeData.getBoundingSphere(loci.volume, boundingSphere);
|
||||
return Volume.getBoundingSphere(loci.volume, boundingSphere);
|
||||
} else if (loci.kind === 'isosurface-loci') {
|
||||
return VolumeData.getBoundingSphere(loci.volume, boundingSphere);
|
||||
return Volume.Isosurface.getBoundingSphere(loci.volume, loci.isoValue, boundingSphere);
|
||||
} else if (loci.kind === 'cell-loci') {
|
||||
// TODO
|
||||
return VolumeData.getBoundingSphere(loci.volume, boundingSphere);
|
||||
return Volume.Cell.getBoundingSphere(loci.volume, loci.indices, boundingSphere);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,6 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
|
||||
if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) {
|
||||
encode_mmCIF_categories_copyAll(encoder, ctx);
|
||||
} else {
|
||||
console.log('default');
|
||||
encode_mmCIF_categories_default(encoder, ctx, params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
|
||||
import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
|
||||
import { CustomProperties } from '../common/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';
|
||||
|
||||
@@ -14,6 +14,10 @@ export const enum Elements {
|
||||
H = 'H', D = 'D', T = 'T', HE = 'HE', LI = 'LI', BE = 'BE', B = 'B', C = 'C', N = 'N', O = 'O', F = 'F', NE = 'NE', NA = 'NA', MG = 'MG', AL = 'AL', SI = 'SI', P = 'P', S = 'S', CL = 'CL', AR = 'AR', K = 'K', CA = 'CA', SC = 'SC', TI = 'TI', V = 'V', CR = 'CR', MN = 'MN', FE = 'FE', CO = 'CO', NI = 'NI', CU = 'CU', ZN = 'ZN', GA = 'GA', GE = 'GE', AS = 'AS', SE = 'SE', BR = 'BR', KR = 'KR', RB = 'RB', SR = 'SR', Y = 'Y', ZR = 'ZR', NB = 'NB', MO = 'MO', TC = 'TC', RU = 'RU', RH = 'RH', PD = 'PD', AG = 'AG', CD = 'CD', IN = 'IN', SN = 'SN', SB = 'SB', TE = 'TE', I = 'I', XE = 'XE', CS = 'CS', BA = 'BA', LA = 'LA', CE = 'CE', PR = 'PR', ND = 'ND', PM = 'PM', SM = 'SM', EU = 'EU', GD = 'GD', TB = 'TB', DY = 'DY', HO = 'HO', ER = 'ER', TM = 'TM', YB = 'YB', LU = 'LU', HF = 'HF', TA = 'TA', W = 'W', RE = 'RE', OS = 'OS', IR = 'IR', PT = 'PT', AU = 'AU', HG = 'HG', TL = 'TL', PB = 'PB', BI = 'BI', PO = 'PO', AT = 'AT', RN = 'RN', FR = 'FR', RA = 'RA', AC = 'AC', TH = 'TH', PA = 'PA', U = 'U', NP = 'NP', PU = 'PU', AM = 'AM', CM = 'CM', BK = 'BK', CF = 'CF', ES = 'ES', FM = 'FM', MD = 'MD', NO = 'NO', LR = 'LR', RF = 'RF', DB = 'DB', SG = 'SG', BH = 'BH', HS = 'HS', MT = 'MT', DS = 'DS', RG = 'RG', CN = 'CN', NH = 'NH', FL = 'FL', MC = 'MC', LV = 'LV', TS = 'TS', OG = 'OG'
|
||||
}
|
||||
|
||||
export const ElementNames: { [k: string]: string } = {
|
||||
H: 'Hydrogen', HE: 'Helium', LI: 'Lithium', BE: 'Beryllium', B: 'Boron', C: 'Carbon', N: 'Nitrogen', O: 'Oxygen', F: 'Fluorine', NE: 'Neon', NA: 'Sodium', MG: 'Magnesium', AL: 'Aluminum', SI: 'Silicon', P: 'Phosphorus', S: 'Sulfur', CL: 'Chlorine', AR: 'Argon', K: 'Potassium', CA: 'Calcium', SC: 'Scandium', TI: 'Titanium', V: 'Vanadium', CR: 'Chromium', MN: 'Manganese', FE: 'Iron', CO: 'Cobalt', NI: 'Nickel', CU: 'Copper', ZN: 'Zinc', GA: 'Gallium', GE: 'Germanium', AS: 'Arsenic', SE: 'Selenium', BR: 'Bromine', KR: 'Krypton', RB: 'Rubidium', SR: 'Strontium', Y: 'Yttrium', ZR: 'Zirconium', NB: 'Niobium', MO: 'Molybdenum', TC: 'Technetium', RU: 'Ruthenium', RH: 'Rhodium', PD: 'Palladium', AG: 'Silver', CD: 'Cadmium', IN: 'Indium', SN: 'Tin', SB: 'Antimony', TE: 'Tellurium', I: 'Iodine', XE: 'Xenon', CS: 'Cesium', BA: 'Barium', LA: 'Lanthanum', CE: 'Cerium', PR: 'Praseodymium', ND: 'Neodymium', PM: 'Promethium', SM: 'Samarium', EU: 'Europium', GD: 'Gadolinium', TB: 'Terbium', DY: 'Dysprosium', HO: 'Holmium', ER: 'Erbium', TM: 'Thulium', YB: 'Ytterbium', LU: 'Lutetium', HF: 'Hafnium', TA: 'Tantalum', W: 'Wolfram', RE: 'Rhenium', OS: 'Osmium', IR: 'Iridium', PT: 'Platinum', AU: 'Gold', HG: 'Mercury', TL: 'Thallium', PB: 'Lead', BI: 'Bismuth', PO: 'Polonium', AT: 'Astatine', RN: 'Radon', FR: 'Francium', RA: 'Radium', AC: 'Actinium', TH: 'Thorium', PA: 'Protactinium', U: 'Uranium', NP: 'Neptunium', PU: 'Plutonium', AM: 'Americium', CM: 'Curium', BK: 'Berkelium', CF: 'Californium', ES: 'Einsteinium', FM: 'Fermium', MD: 'Mendelevium', NO: 'Nobelium', LR: 'Lawrencium', RF: 'Rutherfordium', DB: 'Dubnium', SG: 'Seaborgium', BH: 'Bohrium', HS: 'Hassium', MT: 'Meitnerium', DS: 'Darmstadtium', RG: 'Roentgenium', CN: 'Copernicium', NH: 'Nihonium', FL: 'Flerovium', MC: 'Moscovium', LV: 'Livermorium', TS: 'Tennessine', OG: 'Oganesson'
|
||||
};
|
||||
|
||||
export const AlkaliMetals = new Set<ElementSymbol>(['LI', 'NA', 'K', 'RB', 'CS', 'FR'] as ElementSymbol[]);
|
||||
export function isAlkaliMetal(element: ElementSymbol) { return AlkaliMetals.has(element); }
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import { CustomProperties } from '../common/custom-property';
|
||||
import { AtomicHierarchy } from '../model/properties/atomic';
|
||||
import { StructureSelection } from '../query/selection';
|
||||
import { getBoundary } from '../../../mol-math/geometry/boundary';
|
||||
import { ElementSymbol } from '../model/types';
|
||||
|
||||
class Structure {
|
||||
/** Maps unit.id to unit */
|
||||
@@ -50,6 +51,7 @@ class Structure {
|
||||
masterModel?: Model,
|
||||
representativeModel?: Model,
|
||||
uniqueResidueNames?: Set<string>,
|
||||
uniqueElementSymbols?: Set<ElementSymbol>,
|
||||
entityIndices?: ReadonlyArray<EntityIndex>,
|
||||
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
|
||||
serialMapping?: SerialMapping,
|
||||
@@ -265,6 +267,11 @@ class Structure {
|
||||
|| (this._props.uniqueResidueNames = getUniqueResidueNames(this));
|
||||
}
|
||||
|
||||
get uniqueElementSymbols() {
|
||||
return this._props.uniqueElementSymbols
|
||||
|| (this._props.uniqueElementSymbols = getUniqueElementSymbols(this));
|
||||
}
|
||||
|
||||
get entityIndices() {
|
||||
return this._props.entityIndices
|
||||
|| (this._props.entityIndices = getEntityIndices(this));
|
||||
@@ -403,7 +410,8 @@ function getUniqueResidueNames(s: Structure) {
|
||||
const prop = StructureProperties.residue.label_comp_id;
|
||||
const names = new Set<string>();
|
||||
const loc = StructureElement.Location.create(s);
|
||||
for (const unit of s.units) {
|
||||
for (const unitGroup of s.unitSymmetryGroups) {
|
||||
const unit = unitGroup.units[0];
|
||||
// TODO: support coarse unit?
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
|
||||
@@ -417,6 +425,22 @@ function getUniqueResidueNames(s: Structure) {
|
||||
return names;
|
||||
}
|
||||
|
||||
function getUniqueElementSymbols(s: Structure) {
|
||||
const prop = StructureProperties.atom.type_symbol;
|
||||
const symbols = new Set<ElementSymbol>();
|
||||
const loc = StructureElement.Location.create(s);
|
||||
for (const unitGroup of s.unitSymmetryGroups) {
|
||||
const unit = unitGroup.units[0];
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
loc.unit = unit;
|
||||
for (let i = 0, il = unit.elements.length; i < il; ++i) {
|
||||
loc.element = unit.elements[i];
|
||||
symbols.add(prop(loc));
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
function getEntityIndices(structure: Structure): ReadonlyArray<EntityIndex> {
|
||||
const { units } = structure;
|
||||
const l = StructureElement.Location.create(structure);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { UUID } from '../../../mol-util';
|
||||
import { Column } from '../../../mol-data/db';
|
||||
import { BasicData } from '../../../mol-model-formats/structure/basic/schema';
|
||||
import { ModelFormat } from '../../../mol-model-formats/structure/format';
|
||||
import { ModelFormat } from '../../../mol-model-formats/format';
|
||||
|
||||
export { Topology };
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { SpacegroupCell, Box3D, Sphere3D } from '../../mol-math/geometry';
|
||||
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
|
||||
import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { equalEps } from '../../mol-math/linear-algebra/3d/common';
|
||||
import { ModelFormat } from '../../mol-model-formats/format';
|
||||
|
||||
/** The basic unit cell that contains the data. */
|
||||
interface VolumeDataBase {
|
||||
@@ -20,6 +21,7 @@ interface VolumeDataBase {
|
||||
mean: number,
|
||||
sigma: number
|
||||
}>
|
||||
readonly sourceData: ModelFormat,
|
||||
}
|
||||
|
||||
interface VolumeData extends VolumeDataBase {
|
||||
@@ -28,9 +30,10 @@ interface VolumeData extends VolumeDataBase {
|
||||
|
||||
namespace VolumeData {
|
||||
export const One: VolumeData = {
|
||||
transform: { kind: 'matrix', matrix: Mat4() },
|
||||
transform: { kind: 'matrix', matrix: Mat4.identity() },
|
||||
data: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
|
||||
dataStats: { min: 0, max: 0, mean: 0, sigma: 0 }
|
||||
dataStats: { min: 0, max: 0, mean: 0, sigma: 0 },
|
||||
sourceData: { kind: '', data: '', name: '' }
|
||||
};
|
||||
|
||||
const _scale = Mat4.zero(), _translate = Mat4.zero();
|
||||
@@ -52,12 +55,6 @@ namespace VolumeData {
|
||||
export function areEquivalent(volA: VolumeData, volB: VolumeData) {
|
||||
return volA === volB;
|
||||
}
|
||||
|
||||
export function getBoundingSphere(volume: VolumeData, boundingSphere?: Sphere3D) {
|
||||
if (!boundingSphere) boundingSphere = Sphere3D();
|
||||
// TODO
|
||||
return boundingSphere;
|
||||
}
|
||||
}
|
||||
|
||||
type VolumeIsoValue = VolumeIsoValue.Absolute | VolumeIsoValue.Relative
|
||||
@@ -91,8 +88,8 @@ namespace VolumeIsoValue {
|
||||
|
||||
export function toString(value: VolumeIsoValue) {
|
||||
return value.kind === 'relative'
|
||||
? `${value.relativeValue} σ`
|
||||
: `${value.absoluteValue}`;
|
||||
? `${value.relativeValue.toFixed(2)} σ`
|
||||
: `${value.absoluteValue.toPrecision(4)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,22 +6,62 @@
|
||||
|
||||
import { VolumeData, VolumeIsoValue } from './data';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
import { CubeFormat } from '../../mol-model-formats/volume/cube';
|
||||
|
||||
export namespace Volume {
|
||||
export type CellIndex = { readonly '@type': 'cell-index' } & number
|
||||
|
||||
export function isOrbitals(volume: VolumeData) {
|
||||
if (!CubeFormat.is(volume.sourceData)) return false;
|
||||
return volume.sourceData.data.header.orbitals;
|
||||
}
|
||||
|
||||
export interface Loci { readonly kind: 'volume-loci', readonly volume: VolumeData }
|
||||
export function Loci(volume: VolumeData): Loci { return { kind: 'volume-loci', volume }; }
|
||||
export function isLoci(x: any): x is Loci { return !!x && x.kind === 'volume-loci'; }
|
||||
export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume; }
|
||||
export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
|
||||
|
||||
export function getBoundingSphere(volume: VolumeData, boundingSphere?: Sphere3D) {
|
||||
if (!boundingSphere) boundingSphere = Sphere3D();
|
||||
|
||||
const transform = VolumeData.getGridToCartesianTransform(volume);
|
||||
const [x, y, z] = volume.data.space.dimensions;
|
||||
|
||||
const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
|
||||
const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
|
||||
const cpC = Vec3.create(x, 0, 0); Vec3.transformMat4(cpC, cpC, transform);
|
||||
const cpD = Vec3.create(0, y, z); Vec3.transformMat4(cpD, cpC, transform);
|
||||
|
||||
const cpE = Vec3.create(0, 0, z); Vec3.transformMat4(cpE, cpE, transform);
|
||||
const cpF = Vec3.create(x, 0, z); Vec3.transformMat4(cpF, cpF, transform);
|
||||
const cpG = Vec3.create(x, y, 0); Vec3.transformMat4(cpG, cpG, transform);
|
||||
const cpH = Vec3.create(0, y, 0); Vec3.transformMat4(cpH, cpH, transform);
|
||||
|
||||
const center = Vec3();
|
||||
Vec3.add(center, cpA, cpB);
|
||||
Vec3.scale(center, center, 0.5);
|
||||
const d = Math.max(Vec3.distance(cpA, cpB), Vec3.distance(cpC, cpD));
|
||||
Sphere3D.set(boundingSphere, center, d / 2);
|
||||
Sphere3D.setExtrema(boundingSphere, [cpA, cpB, cpC, cpD, cpE, cpF, cpG, cpH]);
|
||||
|
||||
return boundingSphere;
|
||||
}
|
||||
|
||||
export namespace Isosurface {
|
||||
export interface Loci { readonly kind: 'isosurface-loci', readonly volume: VolumeData, readonly isoValue: VolumeIsoValue }
|
||||
export function Loci(volume: VolumeData, isoValue: VolumeIsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
|
||||
export function isLoci(x: any): x is Loci { return !!x && x.kind === 'isosurface-loci'; }
|
||||
export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && VolumeIsoValue.areSame(a.isoValue, b.isoValue, a.volume.dataStats); }
|
||||
export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
|
||||
|
||||
export function getBoundingSphere(volume: VolumeData, isoValue: VolumeIsoValue, boundingSphere?: Sphere3D) {
|
||||
// TODO get bounding sphere for subgrid with values >= isoValue
|
||||
return Volume.getBoundingSphere(volume, boundingSphere);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Cell {
|
||||
@@ -30,5 +70,30 @@ export namespace Volume {
|
||||
export function isLoci(x: any): x is Loci { return !!x && x.kind === 'cell-loci'; }
|
||||
export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && OrderedSet.areEqual(a.indices, b.indices); }
|
||||
export function isLociEmpty(loci: Loci) { return OrderedSet.size(loci.indices) === 0; }
|
||||
|
||||
const boundaryHelper = new BoundaryHelper('98');
|
||||
const tmpBoundaryPos = Vec3();
|
||||
export function getBoundingSphere(volume: VolumeData, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
|
||||
boundaryHelper.reset();
|
||||
const transform = VolumeData.getGridToCartesianTransform(volume);
|
||||
const { getCoords } = volume.data.space;
|
||||
|
||||
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
|
||||
const o = OrderedSet.getAt(indices, i);
|
||||
getCoords(o, tmpBoundaryPos);
|
||||
Vec3.transformMat4(tmpBoundaryPos, tmpBoundaryPos, transform);
|
||||
boundaryHelper.includePosition(tmpBoundaryPos);
|
||||
}
|
||||
boundaryHelper.finishedIncludeStep();
|
||||
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
|
||||
const o = OrderedSet.getAt(indices, i);
|
||||
getCoords(o, tmpBoundaryPos);
|
||||
Vec3.transformMat4(tmpBoundaryPos, tmpBoundaryPos, transform);
|
||||
boundaryHelper.radiusPosition(tmpBoundaryPos);
|
||||
}
|
||||
|
||||
const bs = boundaryHelper.getSphere(boundingSphere);
|
||||
return Sphere3D.expand(bs, bs, Mat4.getMaxScaleOnAxis(transform) * 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { StateTransforms } from '../transforms';
|
||||
import { Download } from '../transforms/data';
|
||||
import { CustomModelProperties, CustomStructureProperties, TrajectoryFromModelAndCoordinates } from '../transforms/model';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
|
||||
const DownloadModelRepresentationOptions = (plugin: PluginContext) => PD.Group({
|
||||
type: RootStructureDefinition.getParams(void 0, 'auto').type,
|
||||
@@ -26,6 +27,16 @@ const DownloadModelRepresentationOptions = (plugin: PluginContext) => PD.Group({
|
||||
asTrajectory: PD.Optional(PD.Boolean(false, { description: 'Load all entries into a single trajectory.' }))
|
||||
}, { isExpanded: false });
|
||||
|
||||
export const PdbDownloadProvider = {
|
||||
'rcsb': PD.Group({
|
||||
encoding: PD.Select('bcif', [['cif', 'cif'], ['bcif', 'bcif']] as ['cif' | 'bcif', string][]),
|
||||
}, { label: 'RCSB PDB', isFlat: true }),
|
||||
'pdbe': PD.Group({
|
||||
variant: PD.Select('updated-bcif', [['updated-bcif', 'Updated (bcif)'], ['updated', 'Updated'], ['archival', 'Archival']] as ['updated' | 'archival', string][]),
|
||||
}, { label: 'PDBe', isFlat: true }),
|
||||
};
|
||||
export type PdbDownloadProvider = keyof typeof PdbDownloadProvider;
|
||||
|
||||
export { DownloadStructure };
|
||||
type DownloadStructure = typeof DownloadStructure
|
||||
const DownloadStructure = StateAction.build({
|
||||
@@ -33,36 +44,30 @@ const DownloadStructure = StateAction.build({
|
||||
display: { name: 'Download Structure', description: 'Load a structure from the provided source and create its representation.' },
|
||||
params: (_, plugin: PluginContext) => {
|
||||
const options = DownloadModelRepresentationOptions(plugin);
|
||||
const defaultPdbProvider = plugin.config.get(PluginConfig.Download.DefaultPdbProvider) || 'pdbe';
|
||||
return {
|
||||
source: PD.MappedStatic('pdb', {
|
||||
'pdb': PD.Group({
|
||||
provider: PD.Group({
|
||||
id: PD.Text('1tqn', { label: 'PDB Id(s)', description: 'One or more comma separated PDB ids.' }),
|
||||
server: PD.MappedStatic('pdbe', {
|
||||
'rcsb': PD.Group({
|
||||
encoding: PD.Select('bcif', [['cif', 'cif'], ['bcif', 'bcif']] as ['cif' | 'bcif', string][]),
|
||||
}, { label: 'RCSB PDB', isFlat: true }),
|
||||
'pdbe': PD.Group({
|
||||
variant: PD.Select('updated-bcif', [['updated-bcif', 'Updated (bcif)'], ['updated', 'Updated'], ['archival', 'Archival']] as ['updated' | 'archival', string][]),
|
||||
}, { label: 'PDBe', isFlat: true }),
|
||||
}),
|
||||
id: PD.Text('1tqn', { label: 'PDB Id(s)', description: 'One or more comma/space separated PDB ids.' }),
|
||||
server: PD.MappedStatic(defaultPdbProvider, PdbDownloadProvider),
|
||||
}, { pivot: 'id' }),
|
||||
options
|
||||
}, { isFlat: true, label: 'PDB' }),
|
||||
'pdb-dev': PD.Group({
|
||||
id: PD.Text('PDBDEV_00000001', { label: 'PDBDev Id(s)', description: 'One or more comma separated ids.' }),
|
||||
id: PD.Text('PDBDEV_00000001', { label: 'PDBDev Id(s)', description: 'One or more comma/space separated ids.' }),
|
||||
options
|
||||
}, { isFlat: true, label: 'PDBDEV' }),
|
||||
'bcif-static': PD.Group({
|
||||
id: PD.Text('1tqn', { label: 'PDB Id(s)', description: 'One or more comma separated PDB ids.' }),
|
||||
id: PD.Text('1tqn', { label: 'PDB Id(s)', description: 'One or more comma/space separated PDB ids.' }),
|
||||
options
|
||||
}, { isFlat: true, label: 'BinaryCIF (static PDBe Updated)' }),
|
||||
'swissmodel': PD.Group({
|
||||
id: PD.Text('Q9Y2I8', { label: 'UniProtKB AC(s)', description: 'One or more comma separated ACs.' }),
|
||||
id: PD.Text('Q9Y2I8', { label: 'UniProtKB AC(s)', description: 'One or more comma/space separated ACs.' }),
|
||||
options
|
||||
}, { isFlat: true, label: 'SWISS-MODEL', description: 'Loads the best homology model or experimental structure' }),
|
||||
'pubchem': PD.Group({
|
||||
id: PD.Text('2244,2245', { label: 'PubChem ID', description: 'One or more comma separated IDs.' }),
|
||||
id: PD.Text('2244,2245', { label: 'PubChem ID', description: 'One or more comma/space separated IDs.' }),
|
||||
options
|
||||
}, { isFlat: true, label: 'PubChem', description: 'Loads 3D conformer from PubChem.' }),
|
||||
'url': PD.Group({
|
||||
@@ -160,7 +165,7 @@ const DownloadStructure = StateAction.build({
|
||||
}));
|
||||
|
||||
function getDownloadParams(src: string, url: (id: string) => string, label: (id: string) => string, isBinary: boolean): StateTransformer.Params<Download>[] {
|
||||
const ids = src.split(',').map(id => id.trim()).filter(id => !!id && (id.length >= 4 || /^[1-9][0-9]*$/.test(id)));
|
||||
const ids = src.split(/[,\s]/).map(id => id.trim()).filter(id => !!id && (id.length >= 4 || /^[1-9][0-9]*$/.test(id)));
|
||||
const ret: StateTransformer.Params<Download>[] = [];
|
||||
for (const id of ids) {
|
||||
ret.push({ url: Asset.Url(url(id)), isBinary, label: label(id) });
|
||||
|
||||
@@ -16,6 +16,8 @@ import { DataFormatProvider } from '../formats/provider';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { StateTransforms } from '../transforms';
|
||||
|
||||
export type EmdbDownloadProvider = 'pdbe' | 'rcsb'
|
||||
|
||||
export { DownloadDensity };
|
||||
type DownloadDensity = typeof DownloadDensity
|
||||
const DownloadDensity = StateAction.build({
|
||||
@@ -42,7 +44,7 @@ const DownloadDensity = StateAction.build({
|
||||
'pdb-emd-ds': PD.Group({
|
||||
provider: PD.Group({
|
||||
id: PD.Text('emd-8004', { label: 'Id' }),
|
||||
server: PD.Select('pdbe', [['pdbe', 'PDBe'], ['rcsb', 'RCSB PDB']]),
|
||||
server: PD.Select<EmdbDownloadProvider>('pdbe', [['pdbe', 'PDBe'], ['rcsb', 'RCSB PDB']]),
|
||||
}, { pivot: 'id' }),
|
||||
detail: PD.Numeric(3, { min: 0, max: 10, step: 1 }, { label: 'Detail' }),
|
||||
}, { isFlat: true }),
|
||||
|
||||
@@ -75,7 +75,8 @@ const auto = StructureRepresentationPresetProvider({
|
||||
case Structure.Size.Medium:
|
||||
return polymerAndLigand.apply(ref, params, plugin);
|
||||
case Structure.Size.Small:
|
||||
return atomicDetail.apply(ref, params, plugin);
|
||||
// `showCarbohydrateSymbol: true` is nice e.g. for PDB 1aga
|
||||
return atomicDetail.apply(ref, { ...params, showCarbohydrateSymbol: true }, plugin);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -105,7 +106,7 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
nonStandard: await presetStaticComponent(plugin, structureCell, 'non-standard'),
|
||||
branched: await presetStaticComponent(plugin, structureCell, 'branched'),
|
||||
branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
|
||||
water: await presetStaticComponent(plugin, structureCell, 'water'),
|
||||
coarse: await presetStaticComponent(plugin, structureCell, 'coarse')
|
||||
};
|
||||
@@ -115,9 +116,9 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }, { tag: 'polymer' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }, { tag: 'ligand' }),
|
||||
nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }, { tag: 'non-standard' }),
|
||||
branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }, { tag: 'branched-ball-and-stick' }),
|
||||
branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color }, { tag: 'branched-ball-and-stick' }),
|
||||
branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }),
|
||||
water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }, { tag: 'water' }),
|
||||
water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.6 }, color }, { tag: 'water' }),
|
||||
coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' }, { tag: 'coarse' })
|
||||
};
|
||||
|
||||
@@ -226,19 +227,33 @@ const atomicDetail = StructureRepresentationPresetProvider({
|
||||
name: 'Atomic Detail', group: BuiltInPresetGroupName,
|
||||
description: 'Shows everything in atomic detail with Ball & Stick.'
|
||||
},
|
||||
params: () => CommonParams,
|
||||
params: () => ({
|
||||
...CommonParams,
|
||||
showCarbohydrateSymbol: PD.Boolean(false)
|
||||
}),
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
if (!structureCell) return {};
|
||||
|
||||
const components = {
|
||||
all: await presetStaticComponent(plugin, structureCell, 'all'),
|
||||
branched: undefined
|
||||
};
|
||||
if (params.showCarbohydrateSymbol) {
|
||||
Object.assign(components, {
|
||||
branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
|
||||
});
|
||||
}
|
||||
|
||||
const { update, builder, typeParams, color } = reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }, { tag: 'all' })
|
||||
all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }, { tag: 'all' }),
|
||||
};
|
||||
if (params.showCarbohydrateSymbol) {
|
||||
Object.assign(representations, {
|
||||
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
|
||||
});
|
||||
}
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
return { components, representations };
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { shallowMergeArray } from '../mol-util/object';
|
||||
import { RxEventHelper } from '../mol-util/rx-event-helper';
|
||||
import { Subscription, Observable } from 'rxjs';
|
||||
import { arraySetRemove } from '../mol-util/array';
|
||||
|
||||
export class PluginComponent {
|
||||
private _ev: RxEventHelper | undefined;
|
||||
@@ -14,7 +15,18 @@ export class PluginComponent {
|
||||
|
||||
protected subscribe<T>(obs: Observable<T>, action: (v: T) => void) {
|
||||
if (typeof this.subs === 'undefined') this.subs = [];
|
||||
this.subs.push(obs.subscribe(action));
|
||||
|
||||
let sub: Subscription | undefined = obs.subscribe(action);
|
||||
this.subs.push(sub);
|
||||
|
||||
return {
|
||||
unsubscribe: () => {
|
||||
if (sub && this.subs && arraySetRemove(this.subs, sub)) {
|
||||
sub.unsubscribe();
|
||||
sub = void 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected get ev() {
|
||||
|
||||
@@ -114,14 +114,22 @@ export const Provider3dg: TrajectoryFormatProvider = {
|
||||
};
|
||||
|
||||
export const MolProvider: TrajectoryFormatProvider = {
|
||||
label: 'MOL',
|
||||
description: 'MOL',
|
||||
label: 'MOL/SDF',
|
||||
description: 'MOL/SDF',
|
||||
category: Category,
|
||||
stringExtensions: ['mol', 'sdf'],
|
||||
stringExtensions: ['mol', 'sdf', 'sd'],
|
||||
parse: directTrajectory(StateTransforms.Model.TrajectoryFromMOL),
|
||||
visuals: defaultVisuals
|
||||
};
|
||||
|
||||
export const Mol2Provider: TrajectoryFormatProvider = {
|
||||
label: 'MOL2',
|
||||
description: 'MOL2',
|
||||
category: Category,
|
||||
stringExtensions: ['mol2'],
|
||||
parse: directTrajectory(StateTransforms.Model.TrajectoryFromMOL2),
|
||||
visuals: defaultVisuals
|
||||
};
|
||||
|
||||
export const BuiltInTrajectoryFormats = [
|
||||
['mmcif', MmcifProvider] as const,
|
||||
@@ -129,7 +137,8 @@ export const BuiltInTrajectoryFormats = [
|
||||
['pdb', PdbProvider] as const,
|
||||
['gro', GroProvider] as const,
|
||||
['3dg', Provider3dg] as const,
|
||||
['mol', MolProvider] as const
|
||||
['mol', MolProvider] as const,
|
||||
['mol2', Mol2Provider] as const,
|
||||
] as const;
|
||||
|
||||
export type BuiltInTrajectoryFormat = (typeof BuiltInTrajectoryFormats)[number][0]
|
||||
@@ -15,6 +15,7 @@ import { ColorNames } from '../../mol-util/color/names';
|
||||
import { VolumeIsoValue } from '../../mol-model/volume';
|
||||
import { createVolumeRepresentationParams } from '../helpers/volume-representation-params';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
import { Volume } from '../../mol-model/volume/volume';
|
||||
|
||||
const Category = 'Volume';
|
||||
|
||||
@@ -68,9 +69,11 @@ export const DxProvider = DataFormatProvider({
|
||||
stringExtensions: ['dx'],
|
||||
binaryExtensions: ['dxbin'],
|
||||
parse: async (plugin, data) => {
|
||||
const volume = plugin.build()
|
||||
const format = plugin.build()
|
||||
.to(data)
|
||||
.apply(StateTransforms.Volume.VolumeFromDx);
|
||||
.apply(StateTransforms.Data.ParseDx, {}, { state: { isGhost: true } });
|
||||
|
||||
const volume = format.apply(StateTransforms.Volume.VolumeFromDx);
|
||||
|
||||
await volume.commit({ revertOnError: true });
|
||||
|
||||
@@ -102,30 +105,41 @@ export const CubeProvider = DataFormatProvider({
|
||||
visuals: async (plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data>, structure: StateObjectSelector<PluginStateObject.Molecule.Structure> }) => {
|
||||
const surfaces = plugin.build();
|
||||
|
||||
const volumeReprs: StateObjectSelector<PluginStateObject.Volume.Representation3D>[] = [];
|
||||
const volumeData = data.volume.cell?.obj?.data;
|
||||
const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
|
||||
color: 'uniform',
|
||||
colorParams: { value: ColorNames.red }
|
||||
}));
|
||||
const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
|
||||
color: 'uniform',
|
||||
colorParams: { value: ColorNames.blue }
|
||||
}));
|
||||
if (volumeData && Volume.isOrbitals(volumeData)) {
|
||||
const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
|
||||
color: 'uniform',
|
||||
colorParams: { value: ColorNames.blue }
|
||||
}));
|
||||
const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
|
||||
color: 'uniform',
|
||||
colorParams: { value: ColorNames.red }
|
||||
}));
|
||||
volumeReprs.push(volumePos.selector, volumeNeg.selector);
|
||||
} else {
|
||||
const volume = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: VolumeIsoValue.relative(2), alpha: 0.4 },
|
||||
color: 'uniform',
|
||||
colorParams: { value: ColorNames.grey }
|
||||
}));
|
||||
volumeReprs.push(volume.selector);
|
||||
}
|
||||
|
||||
const structure = await plugin.builders.structure.representation.applyPreset(data.structure, 'auto');
|
||||
await surfaces.commit();
|
||||
|
||||
const structureReprs: StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>[] = [];
|
||||
|
||||
objectForEach(structure?.representations as any, (r: any) => {
|
||||
if (r) structureReprs.push(r);
|
||||
});
|
||||
|
||||
return [volumePos.selector, volumeNeg.selector, ...structureReprs];
|
||||
return [...volumeReprs, ...structureReprs];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -162,7 +176,7 @@ export const DscifProvider = DataFormatProvider({
|
||||
if (volumes.length > 0) {
|
||||
visuals[0] = tree
|
||||
.to(volumes[0])
|
||||
.apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 0.3 }, 'uniform', { value: ColorNames.teal }))
|
||||
.apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 1 }, 'uniform', { value: ColorNames.teal }))
|
||||
.selector;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
|
||||
import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
|
||||
import { QueryContext, Structure, StructureQuery, StructureSelection, StructureProperties, StructureElement } from '../../mol-model/structure';
|
||||
import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import Expression from '../../mol-script/language/expression';
|
||||
@@ -15,9 +15,9 @@ import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
import { StateBuilder } from '../../mol-state';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { SetUtils } from '../../mol-util/set';
|
||||
import { stringToWords } from '../../mol-util/string';
|
||||
import { PluginStateObject } from '../objects';
|
||||
import { StateTransforms } from '../transforms';
|
||||
import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
|
||||
|
||||
export enum StructureSelectionCategory {
|
||||
Type = 'Type',
|
||||
@@ -41,6 +41,7 @@ interface StructureSelectionQuery {
|
||||
readonly description: string
|
||||
readonly category: string
|
||||
readonly isHidden: boolean
|
||||
readonly priority: number
|
||||
readonly referencesCurrent: boolean
|
||||
readonly query: StructureQuery
|
||||
readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
|
||||
@@ -51,6 +52,7 @@ interface StructureSelectionQueryProps {
|
||||
description?: string
|
||||
category?: string
|
||||
isHidden?: boolean
|
||||
priority?: number
|
||||
referencesCurrent?: boolean
|
||||
ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
|
||||
}
|
||||
@@ -63,6 +65,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
|
||||
description: props.description || '',
|
||||
category: props.category ?? StructureSelectionCategory.Misc,
|
||||
isHidden: !!props.isHidden,
|
||||
priority: props.priority || 0,
|
||||
referencesCurrent: !!props.referencesCurrent,
|
||||
get query() {
|
||||
if (!_query) _query = compile<StructureSelection>(expression);
|
||||
@@ -81,7 +84,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
|
||||
};
|
||||
}
|
||||
|
||||
const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '' });
|
||||
const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '', priority: 1000 });
|
||||
const current = StructureSelectionQuery('Current Selection', MS.internal.generator.current(), { category: '', referencesCurrent: true });
|
||||
|
||||
const polymer = StructureSelectionQuery('Polymer', MS.struct.modifier.union([
|
||||
@@ -229,14 +232,14 @@ const branchedPlusConnected = StructureSelectionQuery('Carbohydrate with Connect
|
||||
MS.struct.modifier.includeConnected({
|
||||
0: branched.expression, 'layer-count': 1, 'as-whole-residues': true
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Internal });
|
||||
]), { category: StructureSelectionCategory.Internal, isHidden: true });
|
||||
|
||||
const branchedConnectedOnly = StructureSelectionQuery('Connected to Carbohydrate', MS.struct.modifier.union([
|
||||
MS.struct.modifier.exceptBy({
|
||||
0: branchedPlusConnected.expression,
|
||||
by: branched.expression
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Internal });
|
||||
]), { category: StructureSelectionCategory.Internal, isHidden: true });
|
||||
|
||||
const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
|
||||
MS.struct.combinator.merge([
|
||||
@@ -264,7 +267,7 @@ const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
|
||||
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
|
||||
'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
|
||||
'residue-test': MS.core.str.match([
|
||||
MS.re('non-polymer|(amino|carboxy) terminus', 'i'),
|
||||
MS.re('non-polymer|(amino|carboxy) terminus|peptide-like', 'i'),
|
||||
MS.ammp('chemCompType')
|
||||
])
|
||||
})
|
||||
@@ -290,14 +293,14 @@ const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.
|
||||
]),
|
||||
by: branched.expression
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Internal });
|
||||
]), { category: StructureSelectionCategory.Internal, isHidden: true });
|
||||
|
||||
const ligandConnectedOnly = StructureSelectionQuery('Connected to Ligand', MS.struct.modifier.union([
|
||||
MS.struct.modifier.exceptBy({
|
||||
0: ligandPlusConnected.expression,
|
||||
by: ligand.expression
|
||||
})
|
||||
]), { category: StructureSelectionCategory.Internal });
|
||||
]), { category: StructureSelectionCategory.Internal, isHidden: true });
|
||||
|
||||
// residues connected to ligands or branched entities
|
||||
const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydrate', MS.struct.modifier.union([
|
||||
@@ -305,7 +308,7 @@ const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydra
|
||||
branchedConnectedOnly.expression,
|
||||
ligandConnectedOnly.expression
|
||||
]),
|
||||
]), { category: StructureSelectionCategory.Internal });
|
||||
]), { category: StructureSelectionCategory.Internal, isHidden: true });
|
||||
|
||||
const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.modifier.union([
|
||||
MS.struct.modifier.wholeResidues([
|
||||
@@ -380,45 +383,147 @@ const bonded = StructureSelectionQuery('Residues Bonded to Selection', MS.struct
|
||||
referencesCurrent: true
|
||||
});
|
||||
|
||||
const wholeResidues = StructureSelectionQuery('Whole Residues of Selection', MS.struct.modifier.union([
|
||||
MS.struct.modifier.wholeResidues({
|
||||
0: MS.internal.generator.current()
|
||||
})
|
||||
]), {
|
||||
description: 'Expand current selection to whole residues.',
|
||||
category: StructureSelectionCategory.Manipulate,
|
||||
referencesCurrent: true
|
||||
});
|
||||
|
||||
const StandardAminoAcids = [
|
||||
[['HIS'], 'HISTIDINE'],
|
||||
[['ARG'], 'ARGININE'],
|
||||
[['LYS'], 'LYSINE'],
|
||||
[['ILE'], 'ISOLEUCINE'],
|
||||
[['PHE'], 'PHENYLALANINE'],
|
||||
[['LEU'], 'LEUCINE'],
|
||||
[['TRP'], 'TRYPTOPHAN'],
|
||||
[['ALA'], 'ALANINE'],
|
||||
[['MET'], 'METHIONINE'],
|
||||
[['CYS'], 'CYSTEINE'],
|
||||
[['ASN'], 'ASPARAGINE'],
|
||||
[['VAL'], 'VALINE'],
|
||||
[['GLY'], 'GLYCINE'],
|
||||
[['SER'], 'SERINE'],
|
||||
[['GLN'], 'GLUTAMINE'],
|
||||
[['TYR'], 'TYROSINE'],
|
||||
[['ASP'], 'ASPARTIC ACID'],
|
||||
[['GLU'], 'GLUTAMIC ACID'],
|
||||
[['THR'], 'THREONINE'],
|
||||
[['SEC'], 'SELENOCYSTEINE'],
|
||||
[['PYL'], 'PYRROLYSINE'],
|
||||
[['HIS'], 'Histidine'],
|
||||
[['ARG'], 'Arginine'],
|
||||
[['LYS'], 'Lysine'],
|
||||
[['ILE'], 'Isoleucine'],
|
||||
[['PHE'], 'Phenylalanine'],
|
||||
[['LEU'], 'Leucine'],
|
||||
[['TRP'], 'Tryptophan'],
|
||||
[['ALA'], 'Alanine'],
|
||||
[['MET'], 'Methionine'],
|
||||
[['PRO'], 'Proline'],
|
||||
[['CYS'], 'Cysteine'],
|
||||
[['ASN'], 'Asparagine'],
|
||||
[['VAL'], 'Valine'],
|
||||
[['GLY'], 'Glycine'],
|
||||
[['SER'], 'Serine'],
|
||||
[['GLN'], 'Glutamine'],
|
||||
[['TYR'], 'Tyrosine'],
|
||||
[['ASP'], 'Aspartic Acid'],
|
||||
[['GLU'], 'Glutamic Acid'],
|
||||
[['THR'], 'Threonine'],
|
||||
[['SEC'], 'Selenocysteine'],
|
||||
[['PYL'], 'Pyrrolysine'],
|
||||
[['UNK'], 'Unknown'],
|
||||
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
|
||||
|
||||
const StandardNucleicBases = [
|
||||
[['A', 'DA'], 'ADENOSINE'],
|
||||
[['C', 'DC'], 'CYTIDINE'],
|
||||
[['T', 'DT'], 'THYMIDINE'],
|
||||
[['G', 'DG'], 'GUANOSINE'],
|
||||
[['I', 'DI'], 'INOSINE'],
|
||||
[['U', 'DU'], 'URIDINE'],
|
||||
[['A', 'DA'], 'Adenosine'],
|
||||
[['C', 'DC'], 'Cytidine'],
|
||||
[['T', 'DT'], 'Thymidine'],
|
||||
[['G', 'DG'], 'Guanosine'],
|
||||
[['I', 'DI'], 'Inosine'],
|
||||
[['U', 'DU'], 'Uridine'],
|
||||
[['N', 'DN'], 'Unknown'],
|
||||
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
|
||||
|
||||
function ResidueQuery([names, label]: [string[], string], category: string) {
|
||||
return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
|
||||
export function ResidueQuery([names, label]: [string[], string], category: string, priority = 0) {
|
||||
const description = names.length === 1 && !StandardResidues.has(names[0])
|
||||
? `[${names[0]}] ${label}`
|
||||
: `${label} (${names.join(', ')})`;
|
||||
return StructureSelectionQuery(description, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
|
||||
})
|
||||
]), { category });
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
export function ElementSymbolQuery([names, label]: [string[], string], category: string, priority: number) {
|
||||
const description = `${label} (${names.join(', ')})`;
|
||||
return StructureSelectionQuery(description, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
|
||||
})
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
export function EntityDescriptionQuery([names, label]: [string[], string], category: string, priority: number) {
|
||||
const description = `${label}`;
|
||||
return StructureSelectionQuery(`${label}`, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.list.equal([MS.list(...names), MS.ammp('entityDescription')])
|
||||
})
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
const StandardResidues = SetUtils.unionMany(
|
||||
AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
|
||||
);
|
||||
|
||||
export function getElementQueries(structures: Structure[]) {
|
||||
const uniqueElements = new Set<ElementSymbol>();
|
||||
for (const structure of structures) {
|
||||
structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
|
||||
}
|
||||
|
||||
const queries: StructureSelectionQuery[] = [];
|
||||
uniqueElements.forEach(e => {
|
||||
const label = ElementNames[e] || e;
|
||||
queries.push(ElementSymbolQuery([[e], label], 'Element Symbol', 0));
|
||||
});
|
||||
return queries;
|
||||
}
|
||||
|
||||
export function getNonStandardResidueQueries(structures: Structure[]) {
|
||||
const residueLabels = new Map<string, string>();
|
||||
const uniqueResidues = new Set<string>();
|
||||
for (const structure of structures) {
|
||||
structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
|
||||
for (const m of structure.models) {
|
||||
structure.uniqueResidueNames.forEach(r => {
|
||||
const comp = m.properties.chemicalComponentMap.get(r);
|
||||
if (comp) residueLabels.set(r, comp.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const queries: StructureSelectionQuery[] = [];
|
||||
SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
|
||||
const label = residueLabels.get(r) || r;
|
||||
queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue', 200));
|
||||
});
|
||||
return queries;
|
||||
}
|
||||
|
||||
export function getPolymerAndBranchedEntityQueries(structures: Structure[]) {
|
||||
const uniqueEntities = new Map<string, string[]>();
|
||||
const l = StructureElement.Location.create();
|
||||
for (const structure of structures) {
|
||||
l.structure = structure;
|
||||
for (const ug of structure.unitSymmetryGroups) {
|
||||
l.unit = ug.units[0];
|
||||
l.element = ug.elements[0];
|
||||
const entityType = StructureProperties.entity.type(l);
|
||||
if (entityType === 'polymer' || entityType === 'branched') {
|
||||
const description = StructureProperties.entity.pdbx_description(l);
|
||||
uniqueEntities.set(description.join(', '), description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queries: StructureSelectionQuery[] = [];
|
||||
uniqueEntities.forEach((v, k) => {
|
||||
queries.push(EntityDescriptionQuery([v, k], 'Polymer/Carbohydrate Entities', 300));
|
||||
});
|
||||
return queries;
|
||||
}
|
||||
|
||||
export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
|
||||
return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
|
||||
{ expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
|
||||
{ tags: customTag ? [query, customTag] : [query] });
|
||||
}
|
||||
|
||||
export const StructureSelectionQueries = {
|
||||
@@ -447,14 +552,9 @@ export const StructureSelectionQueries = {
|
||||
surroundings,
|
||||
complement,
|
||||
bonded,
|
||||
wholeResidues,
|
||||
};
|
||||
|
||||
export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
|
||||
return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
|
||||
{ expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
|
||||
{ tags: customTag ? [query, customTag] : [query] });
|
||||
}
|
||||
|
||||
export class StructureSelectionQueryRegistry {
|
||||
list: StructureSelectionQuery[] = []
|
||||
options: [StructureSelectionQuery, string, string][] = []
|
||||
|
||||
@@ -44,6 +44,7 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana
|
||||
}
|
||||
|
||||
updateParams(newParams: Partial<PluginAnimationManager.State['params']>) {
|
||||
if (this.isEmpty) return;
|
||||
this.updateState({ params: { ...this.state.params, ...newParams } });
|
||||
const anim = this.map.get(this.state.params.current)!;
|
||||
const params = anim.params(this.context) as PD.Params;
|
||||
@@ -59,6 +60,7 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana
|
||||
}
|
||||
|
||||
updateCurrentParams(values: any) {
|
||||
if (this.isEmpty) return;
|
||||
this._current.paramValues = { ...this._current.paramValues, ...values };
|
||||
this.triggerUpdate();
|
||||
}
|
||||
@@ -163,6 +165,7 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana
|
||||
}
|
||||
|
||||
setSnapshot(snapshot: PluginAnimationManager.Snapshot) {
|
||||
if (this.isEmpty) return;
|
||||
this.updateState({ animationState: snapshot.state.animationState });
|
||||
this.updateParams(snapshot.state.params);
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
|
||||
async setStateSnapshot(snapshot: PluginStateSnapshotManager.StateSnapshot): Promise<PluginState.Snapshot | undefined> {
|
||||
if (snapshot.version !== PLUGIN_VERSION) {
|
||||
// TODO
|
||||
console.warn('state snapshot version mismatch');
|
||||
// console.warn('state snapshot version mismatch');
|
||||
}
|
||||
|
||||
this.clear();
|
||||
|
||||
@@ -37,6 +37,12 @@ export interface StructureMeasurementManagerState {
|
||||
options: StructureMeasurementOptions
|
||||
}
|
||||
|
||||
type StructureMeasurementManagerAddOptions = {
|
||||
customText?: string,
|
||||
selectionTags?: string | string[],
|
||||
reprTags?: string | string[]
|
||||
}
|
||||
|
||||
class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasurementManagerState> {
|
||||
readonly behaviors = {
|
||||
state: this.ev.behavior(this.state)
|
||||
@@ -80,7 +86,7 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree: update, options: { doNotLogTiming: true } });
|
||||
}
|
||||
|
||||
async addDistance(a: StructureElement.Loci, b: StructureElement.Loci) {
|
||||
async addDistance(a: StructureElement.Loci, b: StructureElement.Loci, options?: StructureMeasurementManagerAddOptions) {
|
||||
const cellA = this.plugin.helpers.substructureParent.get(a.structure);
|
||||
const cellB = this.plugin.helpers.substructureParent.get(b.structure);
|
||||
|
||||
@@ -98,17 +104,18 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
],
|
||||
isTransitive: true,
|
||||
label: 'Distance'
|
||||
}, { dependsOn })
|
||||
}, { dependsOn, tags: options?.selectionTags })
|
||||
.apply(StateTransforms.Representation.StructureSelectionsDistance3D, {
|
||||
customText: options?.customText || '',
|
||||
unitLabel: this.state.options.distanceUnitLabel,
|
||||
textColor: this.state.options.textColor
|
||||
});
|
||||
}, { tags: options?.reprTags });
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
|
||||
}
|
||||
|
||||
async addAngle(a: StructureElement.Loci, b: StructureElement.Loci, c: StructureElement.Loci) {
|
||||
async addAngle(a: StructureElement.Loci, b: StructureElement.Loci, c: StructureElement.Loci, options?: StructureMeasurementManagerAddOptions) {
|
||||
const cellA = this.plugin.helpers.substructureParent.get(a.structure);
|
||||
const cellB = this.plugin.helpers.substructureParent.get(b.structure);
|
||||
const cellC = this.plugin.helpers.substructureParent.get(c.structure);
|
||||
@@ -129,16 +136,17 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
],
|
||||
isTransitive: true,
|
||||
label: 'Angle'
|
||||
}, { dependsOn })
|
||||
}, { dependsOn, tags: options?.selectionTags })
|
||||
.apply(StateTransforms.Representation.StructureSelectionsAngle3D, {
|
||||
customText: options?.customText || '',
|
||||
textColor: this.state.options.textColor
|
||||
});
|
||||
}, { tags: options?.reprTags });
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
|
||||
}
|
||||
|
||||
async addDihedral(a: StructureElement.Loci, b: StructureElement.Loci, c: StructureElement.Loci, d: StructureElement.Loci) {
|
||||
async addDihedral(a: StructureElement.Loci, b: StructureElement.Loci, c: StructureElement.Loci, d: StructureElement.Loci, options?: StructureMeasurementManagerAddOptions) {
|
||||
const cellA = this.plugin.helpers.substructureParent.get(a.structure);
|
||||
const cellB = this.plugin.helpers.substructureParent.get(b.structure);
|
||||
const cellC = this.plugin.helpers.substructureParent.get(c.structure);
|
||||
@@ -162,16 +170,17 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
],
|
||||
isTransitive: true,
|
||||
label: 'Dihedral'
|
||||
}, { dependsOn })
|
||||
}, { dependsOn, tags: options?.selectionTags })
|
||||
.apply(StateTransforms.Representation.StructureSelectionsDihedral3D, {
|
||||
customText: options?.customText || '',
|
||||
textColor: this.state.options.textColor
|
||||
});
|
||||
}, { tags: options?.reprTags });
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
|
||||
}
|
||||
|
||||
async addLabel(a: StructureElement.Loci) {
|
||||
async addLabel(a: StructureElement.Loci, options?: Omit<StructureMeasurementManagerAddOptions, 'customText'>) {
|
||||
const cellA = this.plugin.helpers.substructureParent.get(a.structure);
|
||||
|
||||
if (!cellA) return;
|
||||
@@ -186,10 +195,10 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
],
|
||||
isTransitive: true,
|
||||
label: 'Label'
|
||||
}, { dependsOn })
|
||||
}, { dependsOn, tags: options?.selectionTags })
|
||||
.apply(StateTransforms.Representation.StructureSelectionsLabel3D, {
|
||||
textColor: this.state.options.textColor
|
||||
});
|
||||
}, { tags: options?.reprTags });
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true } });
|
||||
|
||||
@@ -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>
|
||||
@@ -22,6 +22,7 @@ import { StructureRepresentation, StructureRepresentationState } from '../mol-re
|
||||
import { VolumeRepresentation } from '../mol-repr/volume/representation';
|
||||
import { StateObject, StateTransformer } from '../mol-state';
|
||||
import { CubeFile } from '../mol-io/reader/cube/parser';
|
||||
import { DxFile } from '../mol-io/reader/dx/parser';
|
||||
|
||||
export type TypeClass = 'root' | 'data' | 'prop'
|
||||
|
||||
@@ -73,6 +74,7 @@ export namespace PluginStateObject {
|
||||
export class Ply extends Create<PlyFile>({ name: 'PLY File', typeClass: 'Data' }) { }
|
||||
export class Ccp4 extends Create<Ccp4File>({ name: 'CCP4/MRC/MAP File', typeClass: 'Data' }) { }
|
||||
export class Dsn6 extends Create<Dsn6File>({ name: 'DSN6/BRIX File', typeClass: 'Data' }) { }
|
||||
export class Dx extends Create<DxFile>({ name: 'DX File', typeClass: 'Data' }) { }
|
||||
|
||||
export type BlobEntry = { id: string } & (
|
||||
{ kind: 'json', data: unknown } |
|
||||
@@ -85,6 +87,7 @@ export namespace PluginStateObject {
|
||||
{ kind: 'dcd', data: DcdFile } |
|
||||
{ kind: 'ccp4', data: Ccp4File } |
|
||||
{ kind: 'dsn6', data: Dsn6File } |
|
||||
{ kind: 'dx', data: DxFile } |
|
||||
{ kind: 'ply', data: PlyFile } |
|
||||
// For non-build in extensions
|
||||
{ kind: 'custom', data: unknown, tag: string }
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { PluginStateObject as SO, PluginStateTransform } from '../objects';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { parseCube } from '../../mol-io/reader/cube/parser';
|
||||
import { parseDx } from '../../mol-io/reader/dx/parser';
|
||||
|
||||
export { Download };
|
||||
export { DownloadBlob };
|
||||
@@ -31,6 +32,7 @@ export { ParsePsf };
|
||||
export { ParsePly };
|
||||
export { ParseCcp4 };
|
||||
export { ParseDsn6 };
|
||||
export { ParseDx };
|
||||
export { ImportString };
|
||||
export { ImportJson };
|
||||
export { ParseJson };
|
||||
@@ -266,7 +268,7 @@ const ParseCube = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse Cube', async ctx => {
|
||||
const parsed = await parseCube(a.data).runInContext(ctx);
|
||||
const parsed = await parseCube(a.data, a.label).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
return new SO.Format.Cube(parsed.result);
|
||||
});
|
||||
@@ -314,7 +316,7 @@ const ParseCcp4 = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse CCP4/MRC/MAP', async ctx => {
|
||||
const parsed = await CCP4.parse(a.data).runInContext(ctx);
|
||||
const parsed = await CCP4.parse(a.data, a.label).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
return new SO.Format.Ccp4(parsed.result);
|
||||
});
|
||||
@@ -330,13 +332,29 @@ const ParseDsn6 = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse DSN6/BRIX', async ctx => {
|
||||
const parsed = await DSN6.parse(a.data).runInContext(ctx);
|
||||
const parsed = await DSN6.parse(a.data, a.label).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
return new SO.Format.Dsn6(parsed.result);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
type ParseDx = typeof ParseDx
|
||||
const ParseDx = PluginStateTransform.BuiltIn({
|
||||
name: 'parse-dx',
|
||||
display: { name: 'Parse DX', description: 'Parse DX from Binary/String data' },
|
||||
from: [SO.Data.Binary, SO.Data.String],
|
||||
to: SO.Format.Dx
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse DX', async ctx => {
|
||||
const parsed = await parseDx(a.data, a.label).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
return new SO.Format.Dx(parsed.result);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
type ImportString = typeof ImportString
|
||||
const ImportString = PluginStateTransform.BuiltIn({
|
||||
name: 'import-string',
|
||||
|
||||
@@ -35,6 +35,8 @@ import { parseMol } from '../../mol-io/reader/mol/parser';
|
||||
import { trajectoryFromMol } from '../../mol-model-formats/structure/mol';
|
||||
import { trajectoryFromCifCore } from '../../mol-model-formats/structure/cif-core';
|
||||
import { trajectoryFromCube } from '../../mol-model-formats/structure/cube';
|
||||
import { parseMol2 } from '../../mol-io/reader/mol2/parser';
|
||||
import { trajectoryFromMol2 } from '../../mol-model-formats/structure/mol2';
|
||||
|
||||
export { CoordinatesFromDcd };
|
||||
export { TopologyFromPsf };
|
||||
@@ -44,6 +46,7 @@ export { TrajectoryFromMmCif };
|
||||
export { TrajectoryFromPDB };
|
||||
export { TrajectoryFromGRO };
|
||||
export { TrajectoryFromMOL };
|
||||
export { TrajectoryFromMOL2 };
|
||||
export { TrajectoryFromCube };
|
||||
export { TrajectoryFromCifCore };
|
||||
export { TrajectoryFrom3DG };
|
||||
@@ -235,6 +238,24 @@ const TrajectoryFromMOL = PluginStateTransform.BuiltIn({
|
||||
}
|
||||
});
|
||||
|
||||
type TrajectoryFromMOL2 = typeof TrajectoryFromMOL
|
||||
const TrajectoryFromMOL2 = PluginStateTransform.BuiltIn({
|
||||
name: 'trajectory-from-mol2',
|
||||
display: { name: 'Parse MOL2', description: 'Parse MOL2 string and create trajectory.' },
|
||||
from: [SO.Data.String],
|
||||
to: SO.Molecule.Trajectory
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse MOL2', async ctx => {
|
||||
const parsed = await parseMol2(a.data, a.label).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
const models = await trajectoryFromMol2(parsed.result).runInContext(ctx);
|
||||
const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` };
|
||||
return new SO.Molecule.Trajectory(models, props);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
type TrajectoryFromCube = typeof TrajectoryFromCube
|
||||
const TrajectoryFromCube = PluginStateTransform.BuiltIn({
|
||||
name: 'trajectory-from-cube',
|
||||
|
||||
@@ -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>
|
||||
@@ -14,7 +14,6 @@ import { Task } from '../../mol-task';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { PluginStateObject as SO, PluginStateTransform } from '../objects';
|
||||
import { volumeFromCube } from '../../mol-model-formats/volume/cube';
|
||||
import { parseDx } from '../../mol-io/reader/dx/parser';
|
||||
import { volumeFromDx } from '../../mol-model-formats/volume/dx';
|
||||
import { VolumeData } from '../../mol-model/volume';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
@@ -42,7 +41,7 @@ const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a, params }) {
|
||||
return Task.create('Create volume from CCP4/MRC/MAP', async ctx => {
|
||||
const volume = await volumeFromCcp4(a.data, { ...params, label: a.label }).runInContext(ctx);
|
||||
const volume = await volumeFromCcp4(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
|
||||
const props = { label: volume.label || 'Volume', description: 'Volume' };
|
||||
return new SO.Volume.Data(volume, props);
|
||||
});
|
||||
@@ -63,7 +62,7 @@ const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a, params }) {
|
||||
return Task.create('Create volume from DSN6/BRIX', async ctx => {
|
||||
const volume = await volumeFromDsn6(a.data, { ...params, label: a.label }).runInContext(ctx);
|
||||
const volume = await volumeFromDsn6(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
|
||||
const props = { label: volume.label || 'Volume', description: 'Volume' };
|
||||
return new SO.Volume.Data(volume, props);
|
||||
});
|
||||
@@ -85,7 +84,7 @@ const VolumeFromCube = PluginStateTransform.BuiltIn({
|
||||
})({
|
||||
apply({ a, params }) {
|
||||
return Task.create('Create volume from Cube', async ctx => {
|
||||
const volume = await volumeFromCube(a.data, { ...params, label: a.label }).runInContext(ctx);
|
||||
const volume = await volumeFromCube(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
|
||||
const props = { label: volume.label || 'Volume', description: 'Volume' };
|
||||
return new SO.Volume.Data(volume, props);
|
||||
});
|
||||
@@ -95,15 +94,15 @@ const VolumeFromCube = PluginStateTransform.BuiltIn({
|
||||
type VolumeFromDx = typeof VolumeFromDx
|
||||
const VolumeFromDx = PluginStateTransform.BuiltIn({
|
||||
name: 'volume-from-dx',
|
||||
display: { name: 'Parse PX', description: 'Parse DX string/binary and create volume.' },
|
||||
from: [SO.Data.String, SO.Data.Binary],
|
||||
display: { name: 'Parse DX', description: 'Create volume from DX data.' },
|
||||
from: SO.Format.Dx,
|
||||
to: SO.Volume.Data
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse DX', async ctx => {
|
||||
const parsed = await parseDx(a.data).runInContext(ctx);
|
||||
if (parsed.isError) throw new Error(parsed.message);
|
||||
const volume = await volumeFromDx(parsed.result, { label: a.label }).runInContext(ctx);
|
||||
console.log(a);
|
||||
const volume = await volumeFromDx(a.data, { label: a.data.name || a.label }).runInContext(ctx);
|
||||
console.log(volume);
|
||||
const props = { label: volume.label || 'Volume', description: 'Volume' };
|
||||
return new SO.Volume.Data(volume, props);
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ import { StructureMeasurementsControls } from './structure/measurements';
|
||||
import { StructureSelectionActionsControls } from './structure/selection';
|
||||
import { StructureSourceControls } from './structure/source';
|
||||
import { VolumeStreamingControls, VolumeSourceControls } from './structure/volume';
|
||||
import { PluginConfig } from '../mol-plugin/config';
|
||||
|
||||
export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> {
|
||||
state = { show: false, label: '' }
|
||||
@@ -219,9 +220,8 @@ export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty:
|
||||
}
|
||||
|
||||
render() {
|
||||
// if (!this.state.show) return null;
|
||||
const isPlaying = this.plugin.managers.snapshot.state.isPlaying;
|
||||
if (isPlaying || this.state.isEmpty) return null;
|
||||
if (isPlaying || this.state.isEmpty || this.plugin.managers.animation.isEmpty || !this.plugin.config.get(PluginConfig.Viewport.ShowAnimation)) return null;
|
||||
|
||||
const isAnimating = this.state.isAnimating;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export class ActionMenu extends React.PureComponent<ActionMenu.Props> {
|
||||
const cmd = this.props;
|
||||
const section = <Section items={cmd.items} onSelect={cmd.onSelect} current={cmd.current} multiselect={this.props.multiselect} noOffset={this.props.noOffset} noAccent={this.props.noAccent} />;
|
||||
return <div className={`msp-action-menu-options${cmd.header ? '' : ' msp-action-menu-options-no-header'}`}>
|
||||
{cmd.header && <ControlGroup header={cmd.header} initialExpanded={true} hideExpander={true} hideOffset onHeaderClick={this.hide} topRightIcon={Close}>
|
||||
{cmd.header && <ControlGroup header={cmd.header} title={cmd.title} initialExpanded={true} hideExpander={true} hideOffset onHeaderClick={this.hide} topRightIcon={Close}>
|
||||
{section}
|
||||
</ControlGroup>}
|
||||
{!cmd.header && section}
|
||||
@@ -33,6 +33,7 @@ export namespace ActionMenu {
|
||||
items: Items,
|
||||
onSelect: OnSelect | OnSelectMany,
|
||||
header?: string,
|
||||
title?: string,
|
||||
current?: Item,
|
||||
multiselect?: boolean,
|
||||
noOffset?: boolean,
|
||||
|
||||
@@ -16,6 +16,7 @@ export type ColorAccent = 'cyan' | 'red' | 'gray' | 'green' | 'purple' | 'blue'
|
||||
|
||||
export class ControlGroup extends React.Component<{
|
||||
header: string,
|
||||
title?: string,
|
||||
initialExpanded?: boolean,
|
||||
hideExpander?: boolean,
|
||||
hideOffset?: boolean,
|
||||
@@ -41,7 +42,7 @@ export class ControlGroup extends React.Component<{
|
||||
|
||||
// TODO: customize header style (bg color, togle button etc)
|
||||
return <div className='msp-control-group-wrapper' style={{ position: 'relative', marginTop: this.props.noTopMargin ? 0 : void 0 }}>
|
||||
<div className='msp-control-group-header' style={{ marginLeft: this.props.headerLeftMargin }}>
|
||||
<div className='msp-control-group-header' style={{ marginLeft: this.props.headerLeftMargin }} title={this.props.title}>
|
||||
<Button onClick={this.headerClicked}>
|
||||
{!this.props.hideExpander && <Icon svg={this.state.isExpanded ? ArrowRight : ArrowDropDown} />}
|
||||
{this.props.topRightIcon && <Icon svg={this.props.topRightIcon} style={{ position: 'absolute', right: '2px', top: 0 }} />}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PluginUIComponent } from '../base';
|
||||
import { StateTransformParameters } from '../state/common';
|
||||
import * as React from 'react';
|
||||
import { VolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/behavior';
|
||||
import { ExpandableControlRow } from '../controls/common';
|
||||
import { ExpandableControlRow, IconButton } from '../controls/common';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ParameterControls, ParamOnChange } from '../controls/parameters';
|
||||
import { Slider } from '../controls/slider';
|
||||
@@ -16,6 +16,10 @@ import { VolumeIsoValue, VolumeData } from '../../mol-model/volume';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { toPrecision } from '../../mol-util/number';
|
||||
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
|
||||
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
|
||||
import { StateSelection, StateObjectCell } from '../../mol-state';
|
||||
import { setSubtreeVisibility } from '../../mol-plugin/behavior/static/state';
|
||||
|
||||
const ChannelParams = {
|
||||
color: PD.Color(ColorNames.black, { description: 'Display color of the volume.' }),
|
||||
@@ -24,36 +28,69 @@ const ChannelParams = {
|
||||
};
|
||||
type ChannelParams = PD.Values<typeof ChannelParams>
|
||||
|
||||
function Channel(props: {
|
||||
class Channel extends PluginUIComponent<{
|
||||
label: string,
|
||||
name: VolumeStreaming.ChannelType,
|
||||
channels: { [k: string]: VolumeStreaming.ChannelParams },
|
||||
isRelative: boolean,
|
||||
params: StateTransformParameters.Props,
|
||||
stats: VolumeData['dataStats'],
|
||||
changeIso: (name: string, value: number, isRelative: boolean) => void
|
||||
changeParams: (name: string, param: string, value: any) => void
|
||||
}) {
|
||||
const { isRelative, stats } = props;
|
||||
const channel = props.channels[props.name]!;
|
||||
changeIso: (name: string, value: number, isRelative: boolean) => void,
|
||||
changeParams: (name: string, param: string, value: any) => void,
|
||||
bCell: StateObjectCell,
|
||||
isDisabled?: boolean
|
||||
}> {
|
||||
private ref = StateSelection.findTagInSubtree(this.plugin.state.data.tree, this.props.bCell!.transform.ref, this.props.name);
|
||||
|
||||
const { min, max, mean, sigma } = stats;
|
||||
const value = Math.round(100 * (channel.isoValue.kind === 'relative' ? channel.isoValue.relativeValue : channel.isoValue.absoluteValue)) / 100;
|
||||
const relMin = (min - mean) / sigma;
|
||||
const relMax = (max - mean) / sigma;
|
||||
const step = toPrecision(isRelative ? Math.round(((max - min) / sigma)) / 100 : sigma / 100, 2);
|
||||
componentDidUpdate() {
|
||||
this.ref = StateSelection.findTagInSubtree(this.plugin.state.data.tree, this.props.bCell!.transform.ref, this.props.name);
|
||||
}
|
||||
|
||||
return <ExpandableControlRow
|
||||
label={props.label + (props.isRelative ? ' \u03C3' : '')}
|
||||
colorStripe={channel.color}
|
||||
pivot={<Slider value={value} min={isRelative ? relMin : min} max={isRelative ? relMax : max} step={step}
|
||||
onChange={v => props.changeIso(props.name, v, isRelative)} disabled={props.params.isDisabled} onEnter={props.params.events.onEnter} />}
|
||||
controls={<ParameterControls onChange={({ name, value }) => props.changeParams(props.name, name, value)} params={ChannelParams} values={channel} onEnter={props.params.events.onEnter} />}
|
||||
/>;
|
||||
componentDidMount() {
|
||||
this.subscribe(this.plugin.state.data.events.cell.stateUpdated, e => {
|
||||
if (this.ref === e.ref) this.forceUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
getVisible = () => {
|
||||
const state = this.plugin.state.data;
|
||||
const ref = this.ref;
|
||||
if (!ref) return false;
|
||||
return !state.cells.get(ref)!.state.isHidden;
|
||||
};
|
||||
|
||||
toggleVisible = () => {
|
||||
const state = this.plugin.state.data;
|
||||
const ref = this.ref;
|
||||
if (!ref) return;
|
||||
setSubtreeVisibility(state, ref, !state.cells.get(ref)!.state.isHidden);
|
||||
};
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const { isRelative, stats } = props;
|
||||
const channel = props.channels[props.name]!;
|
||||
|
||||
const { min, max, mean, sigma } = stats;
|
||||
const value = Math.round(100 * (channel.isoValue.kind === 'relative' ? channel.isoValue.relativeValue : channel.isoValue.absoluteValue)) / 100;
|
||||
const relMin = (min - mean) / sigma;
|
||||
const relMax = (max - mean) / sigma;
|
||||
const step = toPrecision(isRelative ? Math.round(((max - min) / sigma)) / 100 : sigma / 100, 2);
|
||||
|
||||
return <ExpandableControlRow
|
||||
label={props.label + (props.isRelative ? ' \u03C3' : '')}
|
||||
colorStripe={channel.color}
|
||||
pivot={<div className='msp-volume-channel-inline-controls'>
|
||||
<Slider value={value} min={isRelative ? relMin : min} max={isRelative ? relMax : max} step={step}
|
||||
onChange={v => props.changeIso(props.name, v, isRelative)} disabled={props.params.isDisabled} onEnter={props.params.events.onEnter} />
|
||||
<IconButton svg={this.getVisible() ? VisibilityOutlined : VisibilityOffOutlined} onClick={this.toggleVisible} toggleState={false} disabled={props.params.isDisabled} />
|
||||
</div>}
|
||||
controls={<ParameterControls onChange={({ name, value }) => props.changeParams(props.name, name, value)} params={ChannelParams} values={channel} onEnter={props.params.events.onEnter} isDisabled={props.params.isDisabled} />}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransformParameters.Props> {
|
||||
|
||||
private areInitial(params: any) {
|
||||
return PD.areEqual(this.props.info.params, params, this.props.info.initialValues);
|
||||
}
|
||||
@@ -170,8 +207,8 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
|
||||
|
||||
render() {
|
||||
if (!this.props.b) return null;
|
||||
|
||||
const b = (this.props.b as VolumeStreaming).data;
|
||||
|
||||
const isEM = b.info.kind === 'em';
|
||||
const pivot = isEM ? 'em' : '2fo-fc';
|
||||
|
||||
@@ -225,16 +262,16 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
|
||||
};
|
||||
|
||||
if (isOff) {
|
||||
return <ParameterControls onChange={this.changeOption} params={OptionsParams} values={options} onEnter={this.props.events.onEnter} />;
|
||||
return <ParameterControls onChange={this.changeOption} params={OptionsParams} values={options} onEnter={this.props.events.onEnter} isDisabled={this.props.isDisabled} />;
|
||||
}
|
||||
|
||||
return <>
|
||||
{!isEM && <Channel label='2Fo-Fc' name='2fo-fc' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
|
||||
{!isEM && <Channel label='Fo-Fc(+ve)' name='fo-fc(+ve)' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
|
||||
{!isEM && <Channel label='Fo-Fc(-ve)' name='fo-fc(-ve)' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
|
||||
{isEM && <Channel label='EM' name='em' channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
|
||||
{!isEM && <Channel label='2Fo-Fc' name='2fo-fc' bCell={this.props.bCell!} channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
|
||||
{!isEM && <Channel label='Fo-Fc(+ve)' name='fo-fc(+ve)' bCell={this.props.bCell!} channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
|
||||
{!isEM && <Channel label='Fo-Fc(-ve)' name='fo-fc(-ve)' bCell={this.props.bCell!} channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[1]} />}
|
||||
{isEM && <Channel label='EM' name='em' bCell={this.props.bCell!} channels={params.entry.params.channels} changeIso={this.changeIso} changeParams={this.changeParams} isRelative={isRelative} params={this.props} stats={sampling.valuesInfo[0]} />}
|
||||
|
||||
<ParameterControls onChange={this.changeOption} params={OptionsParams} values={options} onEnter={this.props.events.onEnter} />
|
||||
<ParameterControls onChange={this.changeOption} params={OptionsParams} values={options} onEnter={this.props.events.onEnter} isDisabled={this.props.isDisabled} />
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -88,21 +88,18 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// .msp-btn-icon, .msp-btn-icon-small {
|
||||
// svg {
|
||||
// display: inline-flex;
|
||||
// vertical-align: middle;
|
||||
// font-size: 1rem;
|
||||
// margin-bottom: 3px;
|
||||
// }
|
||||
// }
|
||||
|
||||
.msp-material-icon {
|
||||
svg {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
font-size: 1rem;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 3px;
|
||||
|
||||
fill: currentColor;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +267,7 @@ select[multiple],
|
||||
select[size] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
// Reset height for `textarea`s
|
||||
textarea.msp-form-control {
|
||||
height: auto;
|
||||
|
||||
@@ -579,4 +579,35 @@
|
||||
@include accent('green', $color-accent-green);
|
||||
@include accent('purple', $color-accent-purple);
|
||||
@include accent('blue', $color-accent-blue);
|
||||
@include accent('orange', $color-accent-orange);
|
||||
@include accent('orange', $color-accent-orange);
|
||||
|
||||
.msp-volume-channel-inline-controls {
|
||||
> :first-child {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: $row-height;
|
||||
right: 32px;
|
||||
}
|
||||
|
||||
.msp-slider {
|
||||
> div:first-child() {
|
||||
right: 42px;
|
||||
}
|
||||
> div:last-child() {
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
> button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 32px;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
|
||||
.msp-material-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user