Compare commits

...

110 Commits

Author SHA1 Message Date
David Sehnal
b14b5ca626 0.7.0-dev.13 2020-04-27 02:05:52 +02:00
David Sehnal
ffbaa944f2 tweak publish scripts 2020-04-27 02:03:47 +02:00
David Sehnal
e2ba96174a Merge branch 'master' of https://github.com/molstar/molstar 2020-04-27 02:02:31 +02:00
David Sehnal
8c5d99bb54 higher quality logo, add link, move to corner 2020-04-27 02:02:21 +02:00
Alexander Rose
b18b3be070 basic mol2 format support 2020-04-26 14:08:19 -07:00
Alexander Rose
2e69b7c419 CubeProvider tweaks, check if orbitals 2020-04-26 13:06:35 -07:00
Alexander Rose
5007f5fb72 added VolumeData.sourceData: ModelFormat 2020-04-26 13:05:39 -07:00
Alexander Rose
6fe83a9a70 cellpack: fixed pdb fallback when opm fails 2020-04-26 12:27:30 -07:00
Alexander Rose
20af084127 increate outline threshold max value 2020-04-26 11:49:54 -07:00
Alexander Rose
d6501170e6 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-26 11:34:29 -07:00
Alexander Rose
5f33364514 ignore pickable=false renderObjects completely 2020-04-26 11:34:07 -07:00
David Sehnal
7924c008fa proteopedia-wrapper: return result of snapshot.fetch 2020-04-26 20:32:12 +02:00
David Sehnal
2d2a53f28e 0.7.0-dev.12 2020-04-26 19:00:27 +02:00
David Sehnal
1f7ffabef9 added PhysicalSizeTheme.scale 2020-04-26 18:59:17 +02:00
David Sehnal
16d5c07224 0.7.0-dev.11 2020-04-26 18:04:53 +02:00
David Sehnal
2392bfb579 ParamDefinition.mergeParam fix 2020-04-26 17:54:42 +02:00
David Sehnal
b4036f576c proteopedia-wrapper tweaks 2020-04-26 13:19:10 +02:00
Alexander Rose
690d6812dc cellpack: simple cache to avoid parsing trajectories more than once 2020-04-26 00:56:03 -07:00
Alexander Rose
a44aa02f13 cellpack: support for loading zip files containing model.json and ingredients 2020-04-25 23:50:27 -07:00
Alexander Rose
65ddd6d68a Mol file description and extension tweaks 2020-04-25 15:56:13 -07:00
Alexander Rose
754025b3b1 fix bond label between identically named elements/atoms 2020-04-25 13:11:17 -07:00
Alexander Rose
f0649c5aa3 improved structure selection query labels 2020-04-25 12:51:08 -07:00
Alexander Rose
6df045211c fixed atomicDetail repr preset 2020-04-25 12:50:32 -07:00
Alexander Rose
8a00540de0 0.7.0-dev.10 2020-04-24 19:12:20 -07:00
Alexander Rose
0d78905686 icon css tweak 2020-04-24 19:11:24 -07:00
Alexander Rose
6edab203c2 0.7.0-dev.9 2020-04-24 18:49:20 -07:00
Alexander Rose
0abfdb5ee3 material icon css tweaks 2020-04-24 18:48:17 -07:00
Alexander Rose
88369158c9 0.7.0-dev.8 2020-04-24 18:01:32 -07:00
Alexander Rose
8926575283 larger volume-cell bounding-sphere radius 2020-04-24 18:00:00 -07:00
Alexander Rose
15b0288ce4 selection ui tooltip tweaks 2020-04-24 17:59:39 -07:00
Alexander Rose
28853ec19d 0.7.0-dev.7 2020-04-24 17:30:18 -07:00
Alexander Rose
e3480a076a tooltip tweaks 2020-04-24 16:28:32 -07:00
Alexander Rose
abf6452124 package updates 2020-04-24 16:20:05 -07:00
Alexander Rose
2cdd811dd3 repr preset tweaks- higher opacity since not double-side by default anymore- add snfg3d symbols for auto all-atom preset 2020-04-24 16:12:36 -07:00
Alexander Rose
be6fea39bf fog and image shader tweaks 2020-04-24 15:58:08 -07:00
Alexander Rose
ed1bc2cd07 don't set doubleSided=true when alpha<1 in getQualityProps 2020-04-24 15:57:41 -07:00
Alexander Rose
28d3d5861a add missing , BaseGeometry.CustomQualityParamInfo 2020-04-24 15:56:46 -07:00
Alexander Rose
95d3ef491f structure selection query improvements
- moved out of selection.tsx
- added queries for entities (based on entity description)
2020-04-24 13:21:30 -07:00
Alexander Rose
5c37ddfc6d add names for all elements 2020-04-24 13:20:00 -07:00
Alexander Rose
4d9e2d9c91 support title in ControlGroup and ActionMenu 2020-04-24 13:19:41 -07:00
Alexander Rose
0b1c18913d added MolScript.core.list.equal and MolScript.structureQuery.atomProperty.macromolecular.entityDescription 2020-04-24 12:35:08 -07:00
Alexander Rose
c8c2355d3e 0.7.0-dev.6 2020-04-23 20:43:57 -07:00
Alexander Rose
3d1366024d added more structure selection queries
- whole residues
- non-standard residues from current structures
- elements from current structures
2020-04-23 20:43:00 -07:00
Alexander Rose
f6964d2a66 added Structure.uniqueElementSymbols 2020-04-23 20:42:15 -07:00
Alexander Rose
de60f70af5 relax isApplicable validation-report checks 2020-04-23 16:33:41 -07:00
Alexander Rose
8f2e619162 fix assembly symmetry cage alignement 2020-04-23 16:32:44 -07:00
Alexander Rose
fbcef01c55 0.7.0-dev.5 2020-04-23 14:40:43 -07:00
Alexander Rose
641e0639d4 fix filehandle usage in server/ 2020-04-23 14:39:32 -07:00
Alexander Rose
5048573976 0.7.0-dev.4 2020-04-23 12:53:29 -07:00
Alexander Rose
78e4d8536d tweaked ligand definition 2020-04-23 12:51:52 -07:00
Alexander Rose
a01d088205 allow spaces in download id list 2020-04-23 12:48:23 -07:00
Alexander Rose
4b1d1a045d cube tweaks
- swapped visuals colors
- add orbitals flag to header
- TODO add format data to volumes as in structures
2020-04-23 11:55:58 -07:00
Alexander Rose
e2857d00b4 volume label improvements
- add cell value to loci label
- add file name to volume data objects
2020-04-23 10:51:07 -07:00
Alexander Rose
a55a71d31a bounding sphere calc for volume cell loci 2020-04-23 10:04:43 -07:00
David Sehnal
acf793f112 Tensor.Space.getCoords 2020-04-23 12:21:25 +02:00
Alexander Rose
2d58ea28ea Merge branch 'master' of https://github.com/molstar/molstar 2020-04-22 19:07:18 -07:00
Alexander Rose
b21de78eb5 camera focus for non-structure loci 2020-04-22 19:06:40 -07:00
Alexander Rose
376d4b4ee1 volume improvements and slice repr 2020-04-22 19:06:18 -07:00
Alexander Rose
e39304c7cf add dataOffset method Tensor.Space 2020-04-22 16:02:05 -07:00
Alexander Rose
7cba9cda0c support flipY for textures 2020-04-22 16:01:29 -07:00
Alexander Rose
04c690e8f9 add .writeDepth to renderable state
- renders transparent with writeDepth=true before writeDepth=true
2020-04-22 16:00:38 -07:00
David Sehnal
170d0fbc9d fix VolumeData.One matrix, removed pesky console.logs 2020-04-23 00:28:21 +02:00
David Sehnal
40d632a7b1 Volume streaming UI: toggle channel visibility 2020-04-23 00:17:10 +02:00
David Sehnal
2e754d23f4 0.7.0-dev.3 2020-04-22 18:59:20 +02:00
David Sehnal
5639a4b37c build: plugin version that does not rely on external variables 2020-04-22 14:45:57 +02:00
David Sehnal
8c959f8a60 QueryRuntimeTable.removeSymbol/CustomProp 2020-04-22 14:07:11 +02:00
David Sehnal
d42c9a6e15 0.7.0-dev.2 2020-04-22 13:09:14 +02:00
David Sehnal
da4dabc3f5 fix package.json bin path 2020-04-22 13:08:17 +02:00
David Sehnal
92217905f8 0.7.0-dev.1 2020-04-22 12:27:57 +02:00
Alexander Rose
598441a727 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 18:18:01 -07:00
Alexander Rose
f707cb19a4 volume loci, picking and repr improvements
- Volume.Loci
- Volume.Isosurface.Loci
- Volume.Cell.Loci
- picking
- wip: slice
2020-04-21 18:17:51 -07:00
David Sehnal
63fc408be6 ajaxGet: support custom http headers 2020-04-21 23:36:56 +02:00
David Sehnal
69dedd8c22 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 22:51:22 +02:00
David Sehnal
5480805754 AssignColorVolume
+ UI bugfix
2020-04-21 22:50:49 +02:00
David Sehnal
655ae65b8d dxbin support 2020-04-21 21:59:27 +02:00
David Sehnal
1a23cb672e dx parser 2020-04-21 21:24:59 +02:00
Alexander Rose
19e18b4089 MC: ensure winding-order and normals dir are same for neg/pos iso-level 2020-04-21 11:36:41 -07:00
Alexander Rose
3d909d5012 fix, register Dsn6Provider as dsn6 2020-04-21 11:12:38 -07:00
David Sehnal
ad521948b6 VolumeData.transform 2020-04-21 20:10:22 +02:00
Alexander Rose
2f3b6a28c1 support older REMARK 350 format, #34 2020-04-21 10:42:05 -07:00
Alexander Rose
c779da674c Merge branch 'master' of https://github.com/molstar/molstar 2020-04-21 10:18:57 -07:00
Alexander Rose
d9b140f9f2 added Model.probablyHasDensityMap 2020-04-21 10:18:39 -07:00
David Sehnal
052648023e VolumeSourceControls
- initial version, needs more work
2020-04-21 19:07:08 +02:00
David Sehnal
f5d12d440e DownloadFile state action 2020-04-21 17:24:56 +02:00
David Sehnal
99d7a90863 Basic cube format support
- TODO: non-orthogonal frames
2020-04-21 17:07:22 +02:00
Alexander Rose
901d5c86e6 wip, stub for volume-slice representation 2020-04-20 19:09:39 -07:00
Alexander Rose
df9efd05e6 image geometry 2020-04-20 19:04:55 -07:00
David Sehnal
26b8adaec4 extensions/cellpack: use plugin.runTask instead of Task.run 2020-04-21 02:43:09 +02:00
David Sehnal
fc6f5a0336 Merge pull request #33 from corredD/forkdev
Forkdev
2020-04-21 02:34:04 +02:00
Alexander Rose
f823a887b7 Merge branch 'master' of https://github.com/molstar/molstar 2020-04-20 10:25:26 -07:00
Alexander Rose
b346d4d85d tweaked selection/focus remove icon 2020-04-20 10:21:43 -07:00
Alexander Rose
70bd035898 shader refactoring
- add dRenderVariant
- add convenince defines
2020-04-20 10:21:04 -07:00
David Sehnal
7e5cdd8e06 mol-plugin-ui: improved selection controls 2020-04-20 18:55:28 +02:00
David Sehnal
a21dac60e0 Rename "full state" to Session
+ ProteopediaWrapper.snapshot tweaks
2020-04-20 18:11:38 +02:00
David Sehnal
9bd2e0d96e typo fix 2020-04-20 17:52:21 +02:00
autin
dd15a000e1 clean the console.log 2020-04-20 13:39:01 +02:00
autin
ebcfa44f22 OPM support 2020-04-20 13:19:21 +02:00
David Sehnal
43845adb71 import materialui icons separately
- avoids loading extra ~5k modules in webpack
2020-04-19 17:02:52 +02:00
David Sehnal
9e3fff65a7 eslint config fix 2020-04-19 16:08:20 +02:00
David Sehnal
05c35a3a3a mol-model: cif export copyAllCategories option
- support in model-server
- moved servers build config
- fixed swagger template not working with the separate server build
2020-04-19 16:01:47 +02:00
David Sehnal
6f46965344 model-server: change response header for query-many exceeded limit 2020-04-19 14:02:05 +02:00
Alexander Rose
27ee576340 improved .hasDensityMap and .isFromPdbArchive helpers 2020-04-18 13:39:44 -07:00
David Sehnal
58492328df max height for Screenshot / State Snapshot panel 2020-04-18 16:01:17 +02:00
David Sehnal
fd102bede1 mol-plugin-ui: close Screenshot/snapshot panel after saving/opening state 2020-04-18 15:54:50 +02:00
David Sehnal
3d8c47eefa proteopedia-wrapper: support zipped state loading 2020-04-18 15:51:25 +02:00
David Sehnal
c4e43228a2 add Component: option to check if an equivalent component already exists 2020-04-18 15:31:51 +02:00
David Sehnal
6526090b8b mol-plugin-ui: added "add representation" to selection mode 2020-04-18 15:03:49 +02:00
David Sehnal
094a018b5b mol-plugin: global state save options 2020-04-18 14:28:58 +02:00
Alexander Rose
a0a9c994b2 basic asset support for volume streaming 2020-04-17 20:37:14 -07:00
Alexander Rose
524ed90e3f lru cache set returns removed entry 2020-04-17 20:36:20 -07:00
208 changed files with 6173 additions and 1998 deletions

View File

@@ -5,7 +5,7 @@
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["tsconfig.json", "src/servers/tsconfig.json"],
"project": ["tsconfig.json", "tsconfig.servers.json"],
"sourceType": "module"
},
"plugins": [

1
.npmignore Normal file
View File

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

2688
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.7.0-dev.0",
"version": "0.7.0-dev.13",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -16,7 +16,7 @@
"test": "npm run lint && jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "tsc --incremental && tsc --build src/servers --incremental",
"build-tsc": "tsc --incremental && tsc --build tsconfig.servers.json --incremental",
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
"build-webpack": "webpack --mode production --config ./webpack.config.production.js",
"build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
@@ -24,7 +24,7 @@
"watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
"watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
"watch-tsc": "tsc --watch --incremental",
"watch-servers": "tsc --build src/servers --watch --incremental",
"watch-servers": "tsc --build tsconfig.servers.json --watch --incremental",
"watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
"watch-webpack": "webpack -w --mode development --display minimal",
"watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js",
@@ -34,9 +34,8 @@
"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/"
@@ -45,7 +44,7 @@
"cif2bcif": "lib/apps/cif2bcif/index.js",
"cifschema": "lib/apps/cifschema/index.js",
"model-server": "lib/servers/servers/model/server.js",
"model-server-query": "lib/servers/servers/model/local.js",
"model-server-query": "lib/servers/servers/model/query.js",
"model-server-preprocess": "lib/servers/servers/model/preprocess.js",
"volume-server": "lib/servers/servers/volume/server.js",
"volume-server-query": "lib/servers/servers/volume/query.js",
@@ -82,51 +81,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",

View File

@@ -34,9 +34,8 @@ function print(data: Volume) {
const { volume_data_3d_info } = data.source;
const row = Table.getRow(volume_data_3d_info, 0);
console.log(row);
console.log(data.volume.cell);
if (data.volume.transform) console.log(data.volume.transform);
console.log(data.volume.dataStats);
console.log(data.volume.fractionalBox);
}
async function doMesh(data: Volume, filename: string) {

View File

@@ -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();
@@ -176,16 +182,19 @@
var snapshot;
addControl('Set Snapshot', () => {
snapshot = PluginWrapper.snapshot.get();
// could use JSON.stringify(snapshot) and upload the data
// const options = { data: true, behavior: false, animation: false, interactivity: false, canvas3d: false, camera: false, cameraTransition: false };
snapshot = PluginWrapper.plugin.state.getSnapshot(/** options */);
// console.log(JSON.stringify(snapshot, null, 2));
});
addControl('Restore Snapshot', () => {
if (!snapshot) return;
PluginWrapper.snapshot.set(snapshot);
});
addControl('Download Snapshot', () => {
snapshot = PluginWrapper.snapshot.download();
addControl('Download State', () => {
PluginWrapper.snapshot.download('molj');
});
addControl('Download Session', () => {
PluginWrapper.snapshot.download('molx');
});
////////////////////////////////////////////////////////

View File

@@ -5,35 +5,34 @@
*/
import * as ReactDOM from 'react-dom';
import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { EvolutionaryConservation } from './annotation';
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { volumeStreamingControls } from './ui/controls';
import { PluginState } from '../../mol-plugin/state';
import { Scheduler } from '../../mol-task';
import { createProteopediaCustomTheme } from './coloring';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { ColorNames } from '../../mol-util/color/names';
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
import { download } from '../../mol-util/download';
import { getFormattedTime } from '../../mol-util/date';
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { PluginState } from '../../mol-plugin/state';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { EvolutionaryConservation } from './annotation';
import { createProteopediaCustomTheme } from './coloring';
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
import './index.html';
import { volumeStreamingControls } from './ui/controls';
require('../../mol-plugin-ui/skin/light.scss');
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 5;
static VERSION_MINOR = 2;
static VERSION_MINOR = 5;
private _ev = RxEventHelper.create();
@@ -195,6 +194,7 @@ class MolStarProteopediaWrapper {
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
private emptyLoadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
private loadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
async load({ url, format = 'cif', assemblyId = 'deposited', isBinary = false, representationStyle }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
@@ -232,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) {
@@ -265,26 +264,7 @@ class MolStarProteopediaWrapper {
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { }),
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
// if (!options) {
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: {
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
// clip: DefaultCanvas3DParams.clip
// }
// });
// return;
// }
// options = options || { };
// const props = this.plugin.canvas3d.props;
// const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
// const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
// });
// }
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { })
}
animate = {
@@ -305,11 +285,6 @@ class MolStarProteopediaWrapper {
}
const state = this.state;
// const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
// for (const v of visuals) {
// }
const tree = state.build();
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
@@ -416,22 +391,21 @@ class MolStarProteopediaWrapper {
}
snapshot = {
get: () => {
return this.plugin.state.getSnapshot();
get: (params?: PluginState.SnapshotParams) => {
return this.plugin.state.getSnapshot(params);
},
set: (snapshot: PluginState.Snapshot) => {
return this.plugin.state.setSnapshot(snapshot);
},
download: () => {
const json = JSON.stringify(this.plugin.state.getSnapshot(), null, 2);
const blob = new Blob([json], {type : 'application/json;charset=utf-8'});
download(blob, `mol-star_state_${(name || getFormattedTime())}.json`);
download: async (type: 'molj' | 'molx' = 'molj', params?: PluginState.SnapshotParams) => {
const data = await this.plugin.managers.snapshot.serialize({ type, params });
download(data, `mol-star_state_${(name || getFormattedTime())}.${type}`);
},
fetch: async (url: string) => {
fetch: async (url: string, type: 'molj' | 'molx' = 'molj') => {
try {
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
// TODO: is this OK to test for snapshots from server?
await this.plugin.state.setSnapshot(snapshot?.data?.entries?.[0]?.snapshot || snapshot);
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
this.loadedParams = { ...this.emptyLoadedParams };
return await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
} catch (e) {
console.log(e);
}

View File

@@ -65,6 +65,7 @@ export interface Ingredient {
principalAxis?: Vec3;
/** offset along membrane */
offset?: Vec3;
ingtype?: string;
}
export interface IngredientSource {

View File

@@ -9,7 +9,7 @@ import { PluginContext } from '../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Ingredient, IngredientSource, CellPacking } from './data';
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean } from './util';
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb';
@@ -26,56 +26,85 @@ import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
import { Column } from '../../mol-data/db';
import { createModels } from '../../mol-model-formats/structure/basic/parser';
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
import { Asset, AssetManager } from '../../mol-util/assets';
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(assetManager: AssetManager, id: string, model_id: number, baseUrl: string, file?: Asset.File) {
let model: Model;
class TrajectoryCache {
private map = new Map<string, Model.Trajectory>();
set(id: string, trajectory: Model.Trajectory) { this.map.set(id, trajectory); }
get(id: string) { return this.map.get(id); }
}
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
const assetManager = plugin.managers.asset;
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
let trajectory = trajCache.get(id);
let assets: Asset.Wrapper[] = [];
if (file) {
if (file.name.endsWith('.cif')) {
const text = await assetManager.resolve(file, 'string').run();
assets.push(text);
const cif = (await parseCif(text.data)).blocks[0];
model = (await trajectoryFromMmCIF(cif).run())[model_id];
} else if (file.name.endsWith('.bcif')) {
const binary = await assetManager.resolve(file, 'binary').run();
assets.push(binary);
const cif = (await parseCif(binary.data)).blocks[0];
model = (await trajectoryFromMmCIF(cif).run())[model_id];
} else if (file.name.endsWith('.pdb')) {
const text = await assetManager.resolve(file, 'string').run();
assets.push(text);
const pdb = await parsePDBfile(text.data, id);
model = (await trajectoryFromPDB(pdb).run())[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);
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
}
} else {
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
const { mmcif, asset } = await getFromPdb(id, assetManager);
assets.push(asset);
model = (await trajectoryFromMmCIF(mmcif).run())[model_id];
} else {
const data = await getFromCellPackDB(id, baseUrl, assetManager);
assets.push(data.asset);
if ('pdb' in data) {
model = (await trajectoryFromPDB(data.pdb).run())[model_id];
} else {
model = (await trajectoryFromMmCIF(data.mmcif).run())[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 };
}
async function getStructure(model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model);
const { assembly } = props;
if (assembly) {
structure = await StructureSymmetry.buildAssembly(structure, assembly).run();
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
}
let query;
if (source.selection){
@@ -118,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]));
@@ -260,7 +288,7 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
};
}
async function getCurve(name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model);
const curveModelTask = Task.create('Curve Model', async ctx => {
@@ -269,11 +297,11 @@ async function getCurve(name: string, ingredient: Ingredient, transforms: Mat4[]
return models[0];
});
const curveModel = await curveModelTask.run();
return getStructure(curveModel, ingredient.source);
const curveModel = await plugin.runTask(curveModelTask);
return getStructure(plugin, curveModel, ingredient.source);
}
async function getIngredientStructure(assetManager: AssetManager, 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;
@@ -288,13 +316,12 @@ async function getIngredientStructure(assetManager: AssetManager, ingredient: In
}
// model id in case structure is NMR
const model_id = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const { model, assets } = await getModel(assetManager, source.pdb || name, model_id, baseUrl, file);
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
if (!model) return;
let structure: Structure;
if (nbCurve) {
structure = await getCurve(name, ingredient, getCurveTransforms(ingredient), model);
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
} else {
let bu: string|undefined = source.bu ? source.bu : undefined;
if (bu){
@@ -304,7 +331,7 @@ async function getIngredientStructure(assetManager: AssetManager, ingredient: In
bu = bu.slice(2);
}
}
structure = await getStructure(model, source, { assembly: bu });
structure = await getStructure(plugin, model, source, { assembly: bu });
// transform with offset and pcp
let legacy: boolean = true;
if (ingredient.offset || ingredient.principalAxis){
@@ -336,14 +363,15 @@ async function getIngredientStructure(assetManager: AssetManager, ingredient: In
return { structure, assets };
}
export function createStructureFromCellPack(assetManager: AssetManager, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing;
const assets: Asset.Wrapper[] = [];
const trajCache = new TrajectoryCache();
const structures: Structure[] = [];
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName);
const ingredientStructure = await getIngredientStructure(assetManager, ingredients[iName], baseUrl, ingredientFiles);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
if (ingredientStructure) {
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
@@ -430,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));
@@ -437,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
@@ -455,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)
@@ -483,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({

View File

@@ -71,7 +71,7 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
ingredientFiles[file.name] = file;
}
}
const { structure, assets } = await createStructureFromCellPack(plugin.managers.asset, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
const { structure, assets } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
info: { packingsCount: a.data.packings.length, packingIndex: params.packing }

View File

@@ -9,52 +9,56 @@ import { parsePDB } from '../../mol-io/reader/pdb/parser';
import { AssetManager, Asset } from '../../mol-util/assets';
import { Structure } from '../../mol-model/structure';
import { Vec3 } from '../../mol-math/linear-algebra';
import { PluginContext } from '../../mol-plugin/context';
export async function parseCif(data: string|Uint8Array) {
export async function parseCif(plugin: PluginContext, data: string | Uint8Array) {
const comp = CIF.parse(data);
const parsed = await comp.run();
const parsed = await plugin.runTask(comp);
if (parsed.isError) throw parsed;
return parsed.result;
}
export async function parsePDBfile(data: string, id: string) {
export async function parsePDBfile(plugin: PluginContext, data: string, id: string) {
const comp = parsePDB(data, id);
const parsed = await comp.run();
const parsed = await plugin.runTask(comp);
if (parsed.isError) throw parsed;
return parsed.result;
}
async function downloadCif(url: string, isBinary: boolean, assetManager: AssetManager) {
async function downloadCif(plugin: PluginContext, url: string, isBinary: boolean, assetManager: AssetManager) {
const type = isBinary ? 'binary' : 'string';
const asset = await assetManager.resolve(Asset.getUrlAsset(assetManager, url), type).run();
return { cif: await parseCif(asset.data), asset };
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, url), type));
return { cif: await parseCif(plugin, asset.data), asset };
}
async function downloadPDB(url: string, id: string, assetManager: AssetManager) {
async function downloadPDB(plugin: PluginContext, url: string, id: string, assetManager: AssetManager) {
const asset = await assetManager.resolve(Asset.getUrlAsset(assetManager, url), 'string').run();
return { pdb: await parsePDBfile(asset.data, id), asset };
return { pdb: await parsePDBfile(plugin, asset.data, id), asset };
}
export async function getFromPdb(pdbId: string, assetManager: AssetManager) {
const { cif, asset } = await downloadCif(`https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
export async function getFromPdb(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
return { mmcif: cif.blocks[0], asset };
}
export async function getFromCellPackDB(id: string, baseUrl: string, assetManager: AssetManager) {
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager){
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, `https://opm-assets.storage.googleapis.com/pdb/${pdbId.toLowerCase()}.pdb`), 'string'));
return { pdb: await parsePDBfile(plugin, asset.data, pdbId), asset };
}
export async function getFromCellPackDB(plugin: PluginContext, id: string, baseUrl: string, assetManager: AssetManager) {
if (id.toLowerCase().endsWith('.cif') || id.toLowerCase().endsWith('.bcif')) {
const isBinary = id.toLowerCase().endsWith('.bcif');
const { cif, asset } = await downloadCif(`${baseUrl}/other/${id}`, isBinary, assetManager);
const { cif, asset } = await downloadCif(plugin, `${baseUrl}/other/${id}`, isBinary, assetManager);
return { mmcif: cif.blocks[0], asset };
} else {
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id;
return await downloadPDB(`${baseUrl}/other/${name}.pdb`, name, assetManager);
return await downloadPDB(plugin, `${baseUrl}/other/${name}.pdb`, name, assetManager);
}
}
export type IngredientFiles = { [name: string]: Asset.File }
//
export function getStructureMean(structure: Structure) {
let xSum = 0, ySum = 0, zSum = 0;
for (let i = 0, il = structure.units.length; i < il; ++i) {

View File

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

View File

@@ -16,7 +16,8 @@ import { StateAction, StateSelection } from '../../../mol-state';
import { PluginStateObject } from '../../../mol-plugin-state/objects';
import { PluginContext } from '../../../mol-plugin/context';
import { Task } from '../../../mol-task';
import { Check, Extension } from '@material-ui/icons';
import Check from '@material-ui/icons/Check';
import Extension from '@material-ui/icons/Extension';
interface AssemblySymmetryControlState extends CollapsableState {
isBusy: boolean

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

@@ -21,8 +21,9 @@ import { TransformData } from './transform-data';
import { Theme } from '../../mol-theme/theme';
import { RenderObjectValues } from '../../mol-gl/render-object';
import { TextureMesh } from './texture-mesh/texture-mesh';
import { Image } from './image/image';
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh'
export type Geometry<T extends GeometryKind = GeometryKind> =
T extends 'mesh' ? Mesh :
@@ -31,7 +32,8 @@ export type Geometry<T extends GeometryKind = GeometryKind> =
T extends 'text' ? Text :
T extends 'lines' ? Lines :
T extends 'direct-volume' ? DirectVolume :
T extends 'texture-mesh' ? TextureMesh : never
T extends 'image' ? Image :
T extends 'texture-mesh' ? TextureMesh : never
type GeometryParams<T extends GeometryKind> =
T extends 'mesh' ? Mesh.Params :
@@ -40,7 +42,8 @@ type GeometryParams<T extends GeometryKind> =
T extends 'text' ? Text.Params :
T extends 'lines' ? Lines.Params :
T extends 'direct-volume' ? DirectVolume.Params :
T extends 'texture-mesh' ? TextureMesh.Params : never
T extends 'image' ? Image.Params :
T extends 'texture-mesh' ? TextureMesh.Params : never
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryParams<G['kind']>, V = RenderObjectValues<G['kind']>> {
Params: P
@@ -64,6 +67,7 @@ export namespace Geometry {
case 'text': return geometry.charCount * 2 * 3;
case 'lines': return geometry.lineCount * 2 * 3;
case 'direct-volume': return 12 * 3;
case 'image': return 2 * 3;
case 'texture-mesh': return geometry.vertexCount;
}
}
@@ -78,6 +82,8 @@ export namespace Geometry {
return getDrawCount(geometry) === 0 ? 0 : (arrayMax(geometry.groupBuffer.ref.value) + 1);
case 'direct-volume':
return 1;
case 'image':
return arrayMax(geometry.groupTexture.ref.value.array) + 1;
case 'texture-mesh':
return geometry.groupCount;
}
@@ -92,6 +98,7 @@ export namespace Geometry {
case 'text': return Text.Utils as any;
case 'lines': return Lines.Utils as any;
case 'direct-volume': return DirectVolume.Utils as any;
case 'image': return Image.Utils as any;
case 'texture-mesh': return TextureMesh.Utils as any;
}
}

View File

@@ -0,0 +1,210 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { hashFnv32a } from '../../../mol-data/util';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { RenderableState } from '../../../mol-gl/renderable';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { Theme } from '../../../mol-theme/theme';
import { ValueCell } from '../../../mol-util';
import { Color } from '../../../mol-util/color';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { BaseGeometry } from '../base';
import { createColors } from '../color-data';
import { GeometryUtils } from '../geometry';
import { createMarkers } from '../marker-data';
import { createEmptyOverpaint } from '../overpaint-data';
import { TransformData } from '../transform-data';
import { createEmptyTransparency } from '../transparency-data';
import { ImageValues } from '../../../mol-gl/renderable/image';
import { fillSerial } from '../../../mol-util/array';
const QuadIndices = new Uint32Array([
0, 1, 2,
1, 3, 2
]);
const QuadUvs = new Float32Array([
0, 1,
0, 0,
1, 1,
1, 0
]);
export const InterpolationTypes = {
'nearest': 'Nearest',
'catmulrom': 'Catmulrom (Cubic)',
'mitchell': 'Mitchell (Cubic)',
'bspline': 'B-Spline (Cubic)'
};
export type InterpolationTypes = keyof typeof InterpolationTypes;
export const InterpolationTypeNames = Object.keys(InterpolationTypes) as InterpolationTypes[];
export { Image };
interface Image {
readonly kind: 'image',
readonly imageTexture: ValueCell<TextureImage<Float32Array>>,
readonly imageTextureDim: ValueCell<Vec2>,
readonly cornerBuffer: ValueCell<Float32Array>,
readonly groupTexture: ValueCell<TextureImage<Float32Array>>,
/** Bounding sphere of the image */
boundingSphere: Sphere3D
}
namespace Image {
export function create(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image?: Image): Image {
return image ?
update(imageTexture, corners, groupTexture, image) :
fromData(imageTexture, corners, groupTexture);
}
function hashCode(image: Image) {
return hashFnv32a([
image.cornerBuffer.ref.version
]);
}
function fromData(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>): Image {
const boundingSphere = Sphere3D();
let currentHash = -1;
const width = imageTexture.width;
const height = imageTexture.height;
const image = {
kind: 'image' as const,
imageTexture: ValueCell.create(imageTexture),
imageTextureDim: ValueCell.create(Vec2.create(width, height)),
cornerBuffer: ValueCell.create(corners),
groupTexture: ValueCell.create(groupTexture),
get boundingSphere() {
const newHash = hashCode(image);
if (newHash !== currentHash) {
const b = getBoundingSphere(image.cornerBuffer.ref.value);
Sphere3D.copy(boundingSphere, b);
currentHash = newHash;
}
return boundingSphere;
},
};
return image;
}
function update(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
const width = imageTexture.width;
const height = imageTexture.height;
ValueCell.update(image.imageTexture, imageTexture);
ValueCell.update(image.imageTextureDim, Vec2.set(image.imageTextureDim.ref.value, width, height));
ValueCell.update(image.cornerBuffer, corners);
ValueCell.update(image.groupTexture, groupTexture);
return image;
}
export function createEmpty(image?: Image): Image {
return {} as Image; // TODO
}
export const Params = {
...BaseGeometry.Params,
interpolation: PD.Select('bspline', PD.objectToOptions(InterpolationTypes)),
};
export type Params = typeof Params
export const Utils: GeometryUtils<Image, Params> = {
Params,
createEmpty,
createValues,
createValuesSimple,
updateValues,
updateBoundingSphere,
createRenderableState,
updateRenderableState
};
function createValues(image: Image, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): ImageValues {
const { instanceCount, groupCount } = locationIt;
const color = createColors(locationIt, theme.color);
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const counts = { drawCount: QuadIndices.length, groupCount, instanceCount };
const invariantBoundingSphere = Sphere3D.clone(image.boundingSphere);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
...color,
...marker,
...overpaint,
...transparency,
...transform,
...BaseGeometry.createValues(props, counts),
aPosition: image.cornerBuffer,
aUv: ValueCell.create(QuadUvs),
elements: ValueCell.create(QuadIndices),
// aGroup is used as a vertex index here, group id is in tGroupTex
aGroup: ValueCell.create(fillSerial(new Float32Array(4))),
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
dInterpolation: ValueCell.create(props.interpolation),
uImageTexDim: image.imageTextureDim,
tImageTex: image.imageTexture,
tGroupTex: image.groupTexture,
};
}
function createValuesSimple(image: Image, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
const s = BaseGeometry.createSimple(colorValue, sizeValue, transform);
const p = { ...PD.getDefaultValues(Params), ...props };
return createValues(image, s.transform, s.locationIterator, s.theme, p);
}
function updateValues(values: ImageValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.dInterpolation, props.interpolation);
}
function updateBoundingSphere(values: ImageValues, image: Image) {
const invariantBoundingSphere = Sphere3D.clone(image.boundingSphere);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere);
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
}
}
function createRenderableState(props: PD.Values<Params>): RenderableState {
const state = BaseGeometry.createRenderableState(props);
state.opaque = false;
return state;
}
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = false;
}
}
//
function getBoundingSphere(corners: Float32Array) {
return calculateInvariantBoundingSphere(corners, corners.length / 3, 1);
}

View File

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

View File

@@ -196,11 +196,16 @@ class MarchingCubesState {
const n1y = sfg(sf, hi, Math.max(0, hj - 1), hk) - sfg(sf, hi, Math.min(this.nY - 1, hj + 1), hk);
const n1z = sfg(sf, hi, hj, Math.max(0, hk - 1)) - sfg(sf, hi, hj, Math.min(this.nZ - 1, hk + 1));
this.builder.addNormal(
n0x + t * (n0x - n1x),
n0y + t * (n0y - n1y),
n0z + t * (n0z - n1z)
);
const nx = n0x + t * (n0x - n1x);
const ny = n0y + t * (n0y - n1y);
const nz = n0z + t * (n0z - n1z);
// ensure normal-direction is the same for negative and positive iso-levels
if (this.isoLevel >= 0) {
this.builder.addNormal(nx, ny, nz);
} else {
this.builder.addNormal(-nx, -ny, -nz);
}
return id;
}
@@ -255,7 +260,13 @@ class MarchingCubesState {
const triInfo = TriTable[tableIndex];
for (let t = 0; t < triInfo.length; t += 3) {
this.builder.addTriangle(this.vertList, triInfo[t], triInfo[t + 1], triInfo[t + 2], edgeFilter);
const l = triInfo[t], m = triInfo[t + 1], n = triInfo[t + 2];
// ensure winding-order is the same for negative and positive iso-levels
if (this.isoLevel >= 0) {
this.builder.addTriangle(this.vertList, l, m, n, edgeFilter);
} else {
this.builder.addTriangle(this.vertList, n, m, l, edgeFilter);
}
}
}
}

View File

@@ -86,7 +86,8 @@ function createPoints() {
visible: true,
alphaFactor: 1,
pickable: true,
opaque: true
opaque: true,
writeDepth: true
};
return createRenderObject('points', values, state, -1);

View File

@@ -14,6 +14,7 @@ import { LinesValues, LinesRenderable } from './renderable/lines';
import { SpheresValues, SpheresRenderable } from './renderable/spheres';
import { TextValues, TextRenderable } from './renderable/text';
import { TextureMeshValues, TextureMeshRenderable } from './renderable/texture-mesh';
import { ImageValues, ImageRenderable } from './renderable/image';
const getNextId = idFactory(0, 0x7FFFFFFF);
@@ -27,7 +28,7 @@ export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectT
readonly materialId: number
}
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh'
export type RenderObjectValues<T extends RenderObjectType> =
T extends 'mesh' ? MeshValues :
@@ -36,7 +37,8 @@ export type RenderObjectValues<T extends RenderObjectType> =
T extends 'text' ? TextValues :
T extends 'lines' ? LinesValues :
T extends 'direct-volume' ? DirectVolumeValues :
T extends 'texture-mesh' ? TextureMeshValues : never
T extends 'image' ? ImageValues :
T extends 'texture-mesh' ? TextureMeshValues : never
//
@@ -52,6 +54,7 @@ export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext,
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId);
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId);
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId);
case 'image': return ImageRenderable(ctx, o.id, o.values as ImageValues, o.state, o.materialId);
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId);
}
throw new Error('unsupported type');

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { AttributeSpec, Values, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, BaseSchema, UniformSpec } from './schema';
import { ImageShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
import { InterpolationTypeNames } from '../../mol-geo/geometry/image/image';
export const ImageSchema = {
...BaseSchema,
aPosition: AttributeSpec('float32', 3, 0),
aUv: AttributeSpec('float32', 2, 0),
elements: ElementsSpec('uint32'),
uImageTexDim: UniformSpec('v2'),
tImageTex: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
tGroupTex: TextureSpec('image-float32', 'alpha', 'float', 'nearest'),
dInterpolation: DefineSpec('string', InterpolationTypeNames),
};
export type ImageSchema = typeof ImageSchema
export type ImageValues = Values<ImageSchema>
export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValues, state: RenderableState, materialId: number): Renderable<ImageValues> {
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...ImageSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = ImageShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
return createRenderable(renderItem, values, state);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -126,6 +126,10 @@ import direct_volume_vert from './shader/direct-volume.vert';
import direct_volume_frag from './shader/direct-volume.frag';
export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: true });
import image_vert from './shader/image.vert';
import image_frag from './shader/image.frag';
export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { fragDepth: true });
//
export type ShaderDefines = {

View File

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

View File

@@ -1,26 +1,30 @@
export default `
#if defined(dColorType_attribute)
vColor.rgb = aColor;
#elif defined(dColorType_instance)
vColor.rgb = readFromTexture(tColor, aInstance, uColorTexDim).rgb;
#elif defined(dColorType_group)
vColor.rgb = readFromTexture(tColor, group, uColorTexDim).rgb;
#elif defined(dColorType_groupInstance)
vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
#elif defined(dColorType_objectPicking)
vColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dColorType_instancePicking)
vColor = vec4(encodeFloatRGB(aInstance), 1.0);
#elif defined(dColorType_groupPicking)
vColor = vec4(encodeFloatRGB(group), 1.0);
#endif
#if defined(dRenderVariant_color)
#if defined(dColorType_attribute)
vColor.rgb = aColor;
#elif defined(dColorType_instance)
vColor.rgb = readFromTexture(tColor, aInstance, uColorTexDim).rgb;
#elif defined(dColorType_group)
vColor.rgb = readFromTexture(tColor, group, uColorTexDim).rgb;
#elif defined(dColorType_groupInstance)
vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
#endif
#ifdef dOverpaint
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
#endif
#ifdef dOverpaint
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
#endif
#ifdef dTransparency
vGroup = group;
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
#ifdef dTransparency
vGroup = group;
vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
#endif
#elif defined(dRenderVariant_pick)
#if defined(dRenderVariant_pickObject)
vColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
vColor = vec4(encodeFloatRGB(aInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
vColor = vec4(encodeFloatRGB(group), 1.0);
#endif
#endif
`;

View File

@@ -1,43 +1,45 @@
export default `
#if defined(dColorType_uniform)
vec4 material = vec4(uColor, uAlpha);
#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
vec4 material = vec4(vColor.rgb, uAlpha);
#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
vec4 material = uPickable == 1 ? vColor : vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
#elif defined(dColorType_depth)
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
vec4 material = vec4(uColor, uAlpha);
#elif defined(dColorType_varying)
vec4 material = vec4(vColor.rgb, uAlpha);
#endif
// mix material with overpaint
#if defined(dOverpaint)
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
#endif
// apply screendoor transparency
#if defined(dTransparency)
float ta = 1.0 - vTransparency;
float at = 0.0;
// shift by view-offset during multi-sample rendering to allow for blending
vec2 coord = gl_FragCoord.xy + uViewOffset * 0.25;
#if defined(dTransparencyVariant_single)
const mat4 thresholdMatrix = mat4(
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
);
at = thresholdMatrix[int(intMod(coord.x, 4.0))][int(intMod(coord.y, 4.0))];
#elif defined(dTransparencyVariant_multi)
at = fract(dot(vec3(coord, vGroup + 0.5), vec3(2.0, 7.0, 23.0) / 17.0f));
#endif
if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
#endif
#elif defined(dRenderVariant_pick)
vec4 material = vColor;
#elif defined(dRenderVariant_depth)
#ifdef enabledFragDepth
vec4 material = packDepthToRGBA(gl_FragDepthEXT);
#else
vec4 material = packDepthToRGBA(gl_FragCoord.z);
#endif
#endif
// mix material with overpaint
#if defined(dOverpaint) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
#endif
// apply screendoor transparency
#if defined(dTransparency) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
float ta = 1.0 - vTransparency;
float at = 0.0;
// shift by view-offset during multi-sample rendering to allow for blending
vec2 coord = gl_FragCoord.xy + uViewOffset * 0.25;
#if defined(dTransparencyVariant_single)
const mat4 thresholdMatrix = mat4(
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
);
at = thresholdMatrix[int(intMod(coord.x, 4.0))][int(intMod(coord.y, 4.0))];
#elif defined(dTransparencyVariant_multi)
at = fract(dot(vec3(coord, vGroup + 0.5), vec3(2.0, 7.0, 23.0) / 17.0f));
#endif
if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
#endif
`;

View File

@@ -1,22 +1,24 @@
export default `
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
varying vec4 vColor;
#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_varying)
varying vec4 vColor;
#endif
#ifdef dOverpaint
varying vec4 vOverpaint;
#endif
#ifdef dTransparency
varying float vGroup;
varying float vTransparency;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ != 300
varying vec4 vColor;
#else
flat in vec4 vColor;
#endif
#endif
#ifdef dOverpaint
varying vec4 vOverpaint;
#endif
#ifdef dTransparency
varying float vGroup;
varying float vTransparency;
#endif
`;

View File

@@ -1,31 +1,33 @@
export default `
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_attribute)
varying vec4 vColor;
attribute vec3 aColor;
#elif defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
varying vec4 vColor;
uniform vec2 uColorTexDim;
uniform sampler2D tColor;
#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_attribute)
varying vec4 vColor;
attribute vec3 aColor;
#elif defined(dColorType_texture)
varying vec4 vColor;
uniform vec2 uColorTexDim;
uniform sampler2D tColor;
#endif
#ifdef dOverpaint
varying vec4 vOverpaint;
uniform vec2 uOverpaintTexDim;
uniform sampler2D tOverpaint;
#endif
#ifdef dTransparency
varying float vGroup;
varying float vTransparency;
uniform vec2 uTransparencyTexDim;
uniform sampler2D tTransparency;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ != 300
varying vec4 vColor;
#else
flat out vec4 vColor;
#endif
#endif
#ifdef dOverpaint
varying vec4 vOverpaint;
uniform vec2 uOverpaintTexDim;
uniform sampler2D tOverpaint;
#endif
#ifdef dTransparency
varying float vGroup;
varying float vTransparency;
uniform vec2 uTransparencyTexDim;
uniform sampler2D tTransparency;
#endif
`;

View File

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

View File

@@ -1,4 +1,20 @@
export default `
// TODO find a better place for these convenience defines
#if defined(dRenderVariant_pickObject) || defined(dRenderVariant_pickInstance) || defined(dRenderVariant_pickGroup)
#define dRenderVariant_pick
#endif
#if defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
#define dColorType_texture
#endif
#if defined(dColorType_attribute) || defined(dColorType_texture)
#define dColorType_varying
#endif
//
#define PI 3.14159265
#define RECIPROCAL_PI 0.31830988618
#define EPSILON 1e-6

View File

@@ -28,7 +28,6 @@ uniform sampler2D tMarker;
uniform float uAlpha;
uniform float uPickingAlphaThreshold;
uniform int uPickable;
#if defined(dGridTexType_2d)
precision highp sampler2D;
@@ -41,7 +40,7 @@ uniform int uPickable;
#if defined(dColorType_uniform)
uniform vec3 uColor;
#elif defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
#elif defined(dColorType_varying)
uniform vec2 uColorTexDim;
uniform sampler2D tColor;
#endif
@@ -114,23 +113,21 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 viewDir) {
tmp = ((prevValue - uIsoValue) / ((prevValue - uIsoValue) - (value - uIsoValue)));
isoPos = mix(pos - step, pos, tmp);
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#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(dColorType_objectPicking)
#if defined(dRenderVariant_pickObject)
return vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dColorType_instancePicking)
#elif defined(dRenderVariant_pickInstance)
return vec4(encodeFloatRGB(instance), 1.0);
#elif defined(dColorType_groupPicking)
#elif defined(dRenderVariant_pickGroup)
float group = floor(decodeFloatRGB(textureGroup(isoPos).rgb) + 0.5);
return vec4(encodeFloatRGB(group), 1.0);
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
return packDepthToRGBA(gl_FragCoord.z); // TODO calculate isosurface depth
#else
#elif defined(dRenderVariant_color)
// compute gradient by central differences
gradient.x = textureVal(isoPos - dx).a - textureVal(isoPos + dx).a;
gradient.y = textureVal(isoPos - dy).a - textureVal(isoPos + dy).a;

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export default `
precision highp float;
precision highp int;
#include common
#include read_from_texture
#include common_frag_params
uniform vec2 uImageTexDim;
uniform sampler2D tImageTex;
uniform sampler2D tGroupTex;
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
varying vec2 vUv;
varying float vInstance;
#if defined(dInterpolation_catmulrom) || defined(dInterpolation_mitchell) || defined(dInterpolation_bspline)
#define dInterpolation_cubic
#endif
#if defined(dInterpolation_cubic)
#if defined(dInterpolation_catmulrom) || defined(dInterpolation_mitchell)
#if defined(dInterpolation_catmulrom)
const float B = 0.0;
const float C = 0.5;
#elif defined(dInterpolation_mitchell)
const float B = 0.333;
const float C = 0.333;
#endif
float cubicFilter(float x){
float f = x;
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;
}else{
return 0.0;
}
}
#elif defined(dInterpolation_bspline)
float cubicFilter(float x) {
float f = x;
if (f < 0.0) {
f = -f;
}
if (f >= 0.0 && f <= 1.0){
return (2.0 / 3.0) + (0.5) * (f * f * f) - (f * f);
} else if (f > 1.0 && f <= 2.0) {
return 1.0 / 6.0 * pow((2.0 - f), 3.0);
}
return 1.0;
}
#endif
vec4 biCubic(sampler2D tex, vec2 texCoord) {
vec2 texelSize = 1.0 / uImageTexDim;
texCoord -= texelSize / 2.0;
vec4 nSum = vec4(0.0);
float nDenom = 0.0;
vec2 cell = fract(texCoord * uImageTexDim);
for (float m = -1.0; m <= 2.0; ++m) {
for (float n = -1.0; n <= 2.0; ++n) {
vec4 vecData = texture2D(tex, texCoord + texelSize * vec2(m, n));
float c = cubicFilter(m - cell.x) * cubicFilter(-n + cell.y);
nSum += vecData * c;
nDenom += c;
}
}
return nSum / nDenom;
}
#endif
void main() {
#if defined(dInterpolation_cubic)
vec4 imageData = biCubic(tImageTex, vUv);
#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 defined(dRenderVariant_pickObject)
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
#elif defined(dRenderVariant_pickInstance)
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
#elif defined(dRenderVariant_pickGroup)
float group = texture2D(tGroupTex, vUv).r;
gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
#endif
#elif defined(dRenderVariant_depth)
if (imageData.a < 0.05)
discard;
#ifdef enabledFragDepth
gl_FragColor = packDepthToRGBA(gl_FragDepthEXT);
#else
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
#endif
#elif defined(dRenderVariant_color)
if (imageData.a < 0.05)
discard;
gl_FragColor = imageData;
gl_FragColor.a *= uAlpha;
float group = texture2D(tGroupTex, vUv).r;
float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
#include apply_marker_color
#include apply_fog
#endif
}
`;

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export default `
precision highp float;
precision highp int;
#include common
#include common_vert_params
attribute vec3 aPosition;
attribute vec2 aUv;
attribute mat4 aTransform;
attribute float aInstance;
varying vec2 vUv;
varying float vInstance;
void main() {
#include assign_position
vUv = aUv;
vInstance = aInstance;
}
`;

View File

@@ -15,12 +15,12 @@ precision highp int;
void main(){
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_pick)
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#else
#elif defined(dRenderVariant_color)
gl_FragColor = material;
#include apply_marker_color

View File

@@ -33,12 +33,12 @@ void main() {
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_pick)
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#else
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else

View File

@@ -22,12 +22,12 @@ const float radius = 0.5;
void main(){
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_pick)
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#else
#elif defined(dRenderVariant_color)
gl_FragColor = material;
#ifdef dPointFilledCircle

View File

@@ -58,7 +58,7 @@ bool Impostor(out vec3 cameraPos, out vec3 cameraNormal){
cameraPos = rayDirection * negT + rayOrigin;
if (calcDepth(cameraPos) <= 0.0) {
cameraPos = rayDirection * posT + rayOrigin;
interior = true;
@@ -94,12 +94,12 @@ void main(void){
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_pick)
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#else
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else

View File

@@ -56,11 +56,11 @@ void main(){
gl_FragColor = material;
}
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
#if defined(dRenderVariant_pick)
#include check_picking_alpha
#elif defined(dColorType_depth)
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#else
#elif defined(dRenderVariant_color)
#include apply_marker_color
#include apply_fog
#endif

View File

@@ -7,9 +7,9 @@
import { createAttributeBuffers, ElementsBuffer, AttributeKind } from './buffer';
import { createTextures, Texture } from './texture';
import { WebGLContext, checkError } from './context';
import { ShaderCode } from '../shader-code';
import { ShaderCode, DefineValues } from '../shader-code';
import { Program } from './program';
import { RenderableSchema, RenderableValues, AttributeSpec, getValueVersions, splitValues, Values } from '../renderable/schema';
import { RenderableSchema, RenderableValues, AttributeSpec, getValueVersions, splitValues, DefineSpec } from '../renderable/schema';
import { idFactory } from '../../mol-util/id-factory';
import { ValueCell } from '../../mol-util';
import { TextureImage, TextureVolume } from '../../mol-gl/renderable/util';
@@ -46,21 +46,19 @@ export interface RenderItem<T extends string> {
//
const GraphicsRenderVariantDefines = {
'color': {},
'pickObject': { dColorType: ValueCell.create('objectPicking') },
'pickInstance': { dColorType: ValueCell.create('instancePicking') },
'pickGroup': { dColorType: ValueCell.create('groupPicking') },
'depth': { dColorType: ValueCell.create('depth') }
};
export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariantDefines
const GraphicsRenderVariant = { 'color': '', 'pickObject': '', 'pickInstance': '', 'pickGroup': '', 'depth': '' };
export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariant
const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
const ComputeRenderVariantDefines = {
'compute': {},
};
export type ComputeRenderVariant = keyof typeof ComputeRenderVariantDefines
const ComputeRenderVariant = { 'compute': '' };
export type ComputeRenderVariant = keyof typeof ComputeRenderVariant
const ComputeRenderVariants = Object.keys(ComputeRenderVariant) as ComputeRenderVariant[];
type RenderVariantDefines = typeof GraphicsRenderVariantDefines | typeof ComputeRenderVariantDefines
function createProgramVariant(ctx: WebGLContext, variant: string, defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) {
defineValues = { ...defineValues, dRenderVariant: ValueCell.create(variant) };
schema = { ...schema, dRenderVariant: DefineSpec('string') };
return ctx.resources.program(defineValues, shaderCode, schema);
}
//
@@ -90,14 +88,14 @@ function resetValueChanges(valueChanges: ValueChanges) {
//
export type GraphicsRenderItem = RenderItem<keyof typeof GraphicsRenderVariantDefines & string>
export type GraphicsRenderItem = RenderItem<GraphicsRenderVariant>
export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number) {
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, GraphicsRenderVariantDefines);
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, GraphicsRenderVariants);
}
export type ComputeRenderItem = RenderItem<keyof typeof ComputeRenderVariantDefines & string>
export type ComputeRenderItem = RenderItem<ComputeRenderVariant>
export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId = -1) {
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, ComputeRenderVariantDefines);
return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, ComputeRenderVariants);
}
/**
@@ -105,7 +103,7 @@ export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, s
*
* - assumes that `values.drawCount` and `values.instanceCount` exist
*/
export function createRenderItem<T extends RenderVariantDefines, S extends keyof T & string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariantDefines: T): RenderItem<S> {
export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariants: T[]): RenderItem<T> {
const id = getNextRenderItemId();
const { stats, state, resources } = ctx;
const { instancedArrays, vertexArrayObject } = ctx.extensions;
@@ -121,10 +119,9 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
const glDrawMode = getDrawMode(ctx, drawMode);
const programs: ProgramVariants = {};
Object.keys(renderVariantDefines).forEach(k => {
const variantDefineValues: Values<RenderableSchema> = (renderVariantDefines as any)[k];
programs[k] = resources.program({ ...defineValues, ...variantDefineValues }, shaderCode, schema);
});
for (const k of renderVariants) {
programs[k] = createProgramVariant(ctx, k, defineValues, shaderCode, schema);
}
const textures = createTextures(ctx, schema, textureValues);
const attributeBuffers = createAttributeBuffers(ctx, schema, attributeValues);
@@ -136,9 +133,9 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
}
const vertexArrays: VertexArrayVariants = {};
Object.keys(renderVariantDefines).forEach(k => {
for (const k of renderVariants) {
vertexArrays[k] = vertexArrayObject ? resources.vertexArray(programs[k], attributeBuffers, elementsBuffer) : null;
});
}
let drawCount = values.drawCount.ref.value;
let instanceCount = values.instanceCount.ref.value;
@@ -155,9 +152,9 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
return {
id,
materialId,
getProgram: (variant: S) => programs[variant],
getProgram: (variant: T) => programs[variant],
render: (variant: S) => {
render: (variant: T) => {
if (drawCount === 0 || instanceCount === 0 || ctx.isContextLost) return;
const program = programs[variant];
if (program.id === currentProgramId && state.currentRenderItemId === id) {
@@ -216,11 +213,10 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
if (valueChanges.defines) {
// console.log('some defines changed, need to rebuild programs')
Object.keys(renderVariantDefines).forEach(k => {
const variantDefineValues: Values<RenderableSchema> = (renderVariantDefines as any)[k];
for (const k of renderVariants) {
programs[k].destroy();
programs[k] = resources.program({ ...defineValues, ...variantDefineValues }, shaderCode, schema);
});
programs[k] = createProgramVariant(ctx, k, defineValues, shaderCode, schema);
}
}
if (values.drawCount.ref.version !== versions.drawCount) {
@@ -271,11 +267,11 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
if (valueChanges.attributes || valueChanges.defines || valueChanges.elements) {
// console.log('program/defines or buffers changed, update vaos')
Object.keys(renderVariantDefines).forEach(k => {
for (const k of renderVariants) {
const vertexArray = vertexArrays[k];
if (vertexArray) vertexArray.destroy();
vertexArrays[k] = vertexArrayObject ? resources.vertexArray(programs[k], attributeBuffers, elementsBuffer) : null;
});
}
}
for (let i = 0, il = textures.length; i < il; ++i) {
@@ -298,11 +294,11 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
},
destroy: () => {
if (!destroyed) {
Object.keys(renderVariantDefines).forEach(k => {
for (const k of renderVariants) {
programs[k].destroy();
const vertexArray = vertexArrays[k];
if (vertexArray) vertexArray.destroy();
});
}
textures.forEach(([k, texture]) => {
// lifetime of textures with kind 'texture' is defined externally
if (schema[k].kind !== 'texture') {

View File

@@ -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');
}

View File

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

View File

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

View File

@@ -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);
});

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -107,13 +107,22 @@ namespace Tokenizer {
return read;
}
/** Advance the state by the given number of lines and return line starts/ends as tokens. */
export function readLines(state: Tokenizer, count: number): Tokens {
/** Advance the state by the given number of lines and return them*/
export function markLines(state: Tokenizer, count: number): Tokens {
const lineTokens = TokenBuilder.create(state.data, count * 2);
readLinesChunk(state, count, lineTokens);
return lineTokens;
}
/** Advance the state by the given number of lines and return them */
export function readLines(state: Tokenizer, count: number): string[] {
const ret: string[] = [];
for (let i = 0; i < count; i++) {
ret.push(Tokenizer.readLine(state));
}
return ret;
}
/** Advance the state by the given number of lines and return line starts/ends as tokens. */
export async function readLinesAsync(state: Tokenizer, count: number, ctx: RuntimeContext, initialLineCount = 100000): Promise<Tokens> {
const { length } = state;

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from NGL.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Tokenizer } from '../common/text/tokenizer';
import { Column } from '../../../mol-data/db';
import { Task, chunkedSubtask, RuntimeContext } from '../../../mol-task';
import { ReaderResult as Result } from '../result';
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
}
export namespace CubeFile {
export interface Header {
orbitals: boolean,
comment1: string,
comment2: string,
atomCount: number,
origin: Vec3,
dim: Vec3,
basisX: Vec3,
basisY: Vec3,
basisZ: Vec3,
dataSetIds: number[]
}
export interface Atoms {
count: number,
number: Column<number>,
nuclearCharge: Column<number>,
x: Column<number>,
y: Column<number>,
z: Column<number>
}
}
const bohrToAngstromFactor = 0.529177210859;
function readHeader(tokenizer: Tokenizer) {
const headerLines = Tokenizer.readLines(tokenizer, 6);
const h = (k: number, l: number) => {
const field = +headerLines[k].trim().split(/\s+/g)[ l ];
return Number.isNaN(field) ? 0 : field;
};
const basis = (i: number) => {
const n = h(i + 2, 0);
const s = bohrToAngstromFactor;
return [Math.abs(n), Vec3.create(h(i + 2, 1) * s, h(i + 2, 2) * s, h(i + 2, 3) * s), n] as const;
};
const comment1 = headerLines[0].trim();
const comment2 = headerLines[1].trim();
const [atomCount, origin, rawAtomCount] = basis(0);
const [NVX, basisX] = basis(1);
const [NVY, basisY] = basis(2);
const [NVZ, basisZ] = basis(3);
const atoms = readAtoms(tokenizer, atomCount, bohrToAngstromFactor);
const dataSetIds: number[] = [];
if (rawAtomCount >= 0) {
let nVal = h(2, 4);
if (nVal === 0) nVal = 1;
for (let i = 0; i < nVal; i++) dataSetIds.push(i);
} else {
const counts = Tokenizer.readLine(tokenizer).trim().split(/\s+/g);
for (let i = 0, _i = +counts[0]; i < _i; i++) dataSetIds.push(+counts[i + 1]);
}
const header: CubeFile.Header = { orbitals: rawAtomCount < 0, comment1, comment2, atomCount, origin, dim: Vec3.create(NVX, NVY, NVZ), basisX, basisY, basisZ, dataSetIds };
return { header, atoms };
}
function readAtoms(tokenizer: Tokenizer, count: number, scaleFactor: number): CubeFile.Atoms {
const number = new Int32Array(count);
const value = new Float64Array(count);
const x = new Float32Array(count);
const y = new Float32Array(count);
const z = new Float32Array(count);
for (let i = 0; i < count; i++) {
const fields = Tokenizer.readLine(tokenizer).trim().split(/\s+/g);
number[i] = +fields[0];
value[i] = +fields[1];
x[i] = +fields[2] * scaleFactor;
y[i] = +fields[3] * scaleFactor;
z[i] = +fields[4] * scaleFactor;
}
return {
count,
number: Column.ofArray({ array: number, schema: Column.Schema.int }),
nuclearCharge: Column.ofArray({ array: value, schema: Column.Schema.float }),
x: Column.ofArray({ array: x, schema: Column.Schema.float }),
y: Column.ofArray({ array: y, schema: Column.Schema.float }),
z: Column.ofArray({ array: z, schema: Column.Schema.float })
};
}
function readValues(ctx: RuntimeContext, tokenizer: Tokenizer, header: CubeFile.Header) {
const N = header.dim[0] * header.dim[1] * header.dim[2] * header.dataSetIds.length;
const chunkSize = 100 * 100 * 100;
const data = new Float64Array(N);
let offset = 0;
return chunkedSubtask(ctx, chunkSize, data, (count, data) => {
const max = Math.min(N, offset + count);
for (let i = offset; i < max; i++) {
Tokenizer.skipWhitespace(tokenizer);
tokenizer.tokenStart = tokenizer.position;
Tokenizer.eatValue(tokenizer);
data[i] = fastParseFloat(tokenizer.data, tokenizer.tokenStart, tokenizer.tokenEnd);
}
offset = max;
return max === N ? 0 : chunkSize;
}, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
}
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, name });
});
}

View File

@@ -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);
}

View File

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

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from NGL.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../../mol-math/linear-algebra';
import { chunkedSubtask, RuntimeContext, Task } from '../../../mol-task';
import { parseFloat as fastParseFloat } from '../common/text/number-parser';
import { Tokenizer } from '../common/text/tokenizer';
import { ReaderResult as Result } from '../result';
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
}
export namespace DxFile {
export interface Header {
dim: Vec3,
min: Vec3,
h: Vec3
}
}
function readHeader(tokenizer: Tokenizer): { header: DxFile.Header, headerByteCount: number } {
const header: Partial<DxFile.Header> = { h: Vec3() };
let headerByteCount = 0;
let deltaLineCount = 0;
const reWhitespace = /\s+/g;
while (true) {
const line = Tokenizer.readLine(tokenizer);
let ls;
if (line.startsWith('object 1')) {
ls = line.split(reWhitespace);
header.dim = Vec3.create(parseInt(ls[5]), parseInt(ls[6]), parseInt(ls[7]));
} else if (line.startsWith('origin')) {
ls = line.split(reWhitespace);
header.min = Vec3.create(parseFloat(ls[1]), parseFloat(ls[2]), parseFloat(ls[3]));
} else if (line.startsWith('delta')) {
ls = line.split(reWhitespace);
if (deltaLineCount === 0) {
(header.h as any)[0] = parseFloat(ls[1]);
} else if (deltaLineCount === 1) {
(header.h as any)[1] = parseFloat(ls[2]);
} else if (deltaLineCount === 2) {
(header.h as any)[2] = parseFloat(ls[3]);
}
deltaLineCount += 1;
} else if (line.startsWith('object 3')) {
headerByteCount += line.length + 1;
break;
}
headerByteCount += line.length + 1;
}
return { header: header as DxFile.Header, headerByteCount };
}
function readValuesText(ctx: RuntimeContext, tokenizer: Tokenizer, header: DxFile.Header) {
const N = header.dim[0] * header.dim[1] * header.dim[2];
const chunkSize = 100 * 100 * 100;
const data = new Float64Array(N);
let offset = 0;
return chunkedSubtask(ctx, chunkSize, data, (count, data) => {
const max = Math.min(N, offset + count);
for (let i = offset; i < max; i++) {
Tokenizer.skipWhitespace(tokenizer);
tokenizer.tokenStart = tokenizer.position;
Tokenizer.eatValue(tokenizer);
data[i] = fastParseFloat(tokenizer.data, tokenizer.tokenStart, tokenizer.tokenEnd);
}
offset = max;
return max === N ? 0 : chunkSize;
}, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
}
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, name });
}
async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array, name: string) {
await taskCtx.update('Reading header...');
const headerString = utf8Read(data, 0, 1000);
const tokenizer = Tokenizer(headerString);
const { header, headerByteCount } = readHeader(tokenizer);
await taskCtx.update('Reading values...');
const size = header.dim[0] * header.dim[1] * header.dim[2];
const dv = new DataView(data.buffer, data.byteOffset + headerByteCount);
const values = new Float64Array(size);
for (let i = 0; i < size; i++) {
values[i] = dv.getFloat64(i * 8, true);
}
// 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, name });
}
export function parseDx(data: string | Uint8Array, name: string) {
return Task.create<Result<DxFile>>('Parse Cube', taskCtx => {
if (typeof data === 'string') return parseText(taskCtx, data, name);
return parseBinary(taskCtx, data, name);
});
}

View File

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

View File

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

View File

@@ -108,6 +108,11 @@ export namespace Field {
return this;
}
add(field: Field<K, D>) {
this.fields.push(field);
return this;
}
getFields() { return this.fields; }
}
@@ -222,6 +227,8 @@ export namespace Category {
}
export interface Encoder<T = string | Uint8Array> extends EncoderBase {
readonly isBinary: boolean,
setFilter(filter?: Category.Filter): void,
isCategoryIncluded(name: string): boolean,
setFormatter(formatter?: Category.Formatter): void,

View File

@@ -28,6 +28,8 @@ export default class BinaryEncoder implements Encoder<Uint8Array> {
private filter: Category.Filter = Category.DefaultFilter;
private formatter: Category.Formatter = Category.DefaultFormatter;
readonly isBinary = true;
binaryEncodingProvider: BinaryEncodingProvider | undefined = void 0;
setFilter(filter?: Category.Filter) {

View File

@@ -19,6 +19,8 @@ export default class TextEncoder implements Encoder<string> {
private filter: Category.Filter = Category.DefaultFilter;
private formatter: Category.Formatter = Category.DefaultFormatter;
readonly isBinary = false;
binaryEncodingProvider = void 0;
setFilter(filter?: Category.Filter) {

View File

@@ -164,6 +164,21 @@ namespace Mat4 {
return a;
}
export function fromBasis(a: Mat4, x: Vec3, y: Vec3, z: Vec3) {
Mat4.setZero(a);
Mat4.setValue(a, 0, 0, x[0]);
Mat4.setValue(a, 1, 0, x[1]);
Mat4.setValue(a, 2, 0, x[2]);
Mat4.setValue(a, 0, 1, y[0]);
Mat4.setValue(a, 1, 1, y[1]);
Mat4.setValue(a, 2, 1, y[2]);
Mat4.setValue(a, 0, 2, z[0]);
Mat4.setValue(a, 1, 2, z[1]);
Mat4.setValue(a, 2, 2, z[2]);
Mat4.setValue(a, 3, 3, 1);
return a;
}
export function copy(out: Mat4, a: Mat4) {
out[0] = a[0];
out[1] = a[1];

View File

@@ -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;
}
}
});
});

View File

@@ -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[] = [];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Column, Table } from '../../mol-data/db';
import { Model } from '../../mol-model/structure/model';
import { MoleculeType, getElementFromAtomicNumber, ElementSymbol } 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 { CubeFile } from '../../mol-io/reader/cube/parser';
async function getModels(cube: CubeFile, ctx: RuntimeContext): Promise<Model[]> {
const { atoms } = cube;
const MOL = Column.ofConst('MOL', cube.atoms.count, Column.Schema.str);
const A = Column.ofConst('A', cube.atoms.count, Column.Schema.str);
const type_symbol = Column.ofArray({ array: Column.mapToArray(atoms.number, n => getElementFromAtomicNumber(n)), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) });
const seq_id = Column.ofConst(1, atoms.count, Column.Schema.int);
const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
auth_asym_id: A,
auth_atom_id: type_symbol,
auth_comp_id: MOL,
auth_seq_id: seq_id,
Cartn_x: Column.asArrayColumn(atoms.x, Float32Array),
Cartn_y: Column.asArrayColumn(atoms.y, Float32Array),
Cartn_z: Column.asArrayColumn(atoms.z, Float32Array),
id: Column.range(0, atoms.count - 1),
label_asym_id: A,
label_atom_id: type_symbol,
label_comp_id: MOL,
label_seq_id: seq_id,
label_entity_id: Column.ofConst('1', atoms.count, Column.Schema.str),
occupancy: Column.ofConst(1, atoms.count, Column.Schema.float),
type_symbol,
pdbx_PDB_model_num: Column.ofConst(1, 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(seq_id, type_symbol);
componentBuilder.setNames([['MOL', 'Unknown Molecule']]);
componentBuilder.add('MOL', 0);
const basics = createBasic({
entity: entityBuilder.getEntityTable(),
chem_comp: componentBuilder.getChemCompTable(),
atom_site
});
return await createModels(basics, MolFormat.create(cube), ctx);
}
//
export { CubeFormat };
type CubeFormat = ModelFormat<CubeFile>
namespace MolFormat {
export function is(x: ModelFormat): x is CubeFormat {
return x.kind === 'cube';
}
export function create(cube: CubeFile): CubeFormat {
return { kind: 'cube', name: cube.header.comment1, data: cube };
}
}
export function trajectoryFromCube(cube: CubeFile): Task<Model.Trajectory> {
return Task.create('Parse Cube', ctx => getModels(cube, ctx));
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,13 +59,13 @@ export function parseRemark350(lines: Tokens, lineStart: number, lineEnd: number
const assemblies: PdbAssembly[] = [];
// Read the assemblies
let current: PdbAssembly, group: PdbAssembly['groups'][0], matrix: Mat4, operId = 1;
let current: PdbAssembly, group: PdbAssembly['groups'][0], matrix: Mat4, operId = 1, asmId = 1;
const getLine = (n: number) => lines.data.substring(lines.indices[2 * n], lines.indices[2 * n + 1]);
for (let i = lineStart; i < lineEnd; i++) {
let line = getLine(i);
if (line.substr(11, 12) === 'BIOMOLECULE:') {
const id = line.substr(23).trim();
let details: string = `Biomolecule ` + id;
let details = `Biomolecule ${id}`;
line = getLine(i + 1);
if (line.substr(11, 30) !== 'APPLY THE FOLLOWING TO CHAINS:') {
i++;
@@ -100,6 +100,23 @@ export function parseRemark350(lines: Tokens, lineStart: number, lineEnd: number
const c = chainList[j].trim();
if (c) group!.chains.push(c);
}
} else if (line.substr(11, 33) === 'APPLYING THE FOLLOWING TO CHAINS:') {
// variant in older PDB format version
current = PdbAssembly(`${asmId}`, `Biomolecule ${asmId}`);
assemblies.push(current);
asmId += 1;
group = { chains: [], operators: [] };
current!.groups.push(group);
i++;
line = getLine(i);
const chainList = line.substr(11, 69).split(',');
for (let j = 0, jl = chainList.length; j < jl; ++j) {
const c = chainList[j].trim();
if (c) group!.chains.push(c);
}
}
}

View File

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

View File

@@ -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 {
@@ -38,7 +39,7 @@ function getTypedArrayCtor(header: Ccp4Header) {
throw Error(`${valueType} is not a supported value format.`);
}
export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3 }): Task<VolumeData> {
export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<VolumeData> {
return Task.create<VolumeData>('Create Volume Data', async ctx => {
const { header, values } = source;
const size = Vec3.create(header.xLength, header.yLength, header.zLength);
@@ -67,15 +68,32 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
// These, however, calculate sigma, so no data on that.
return {
cell,
fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)),
label: params?.label,
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
data,
dataStats: {
min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
}
},
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 };
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { CubeFile } from '../../mol-io/reader/cube/parser';
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 () => {
const { header, values: sourceValues } = source;
const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
let values: Float64Array;
if (header.dataSetIds.length === 0) {
values = sourceValues;
} else {
// get every nth value from the source values
const [h, k, l] = header.dim;
const nth = (params?.dataIndex || 0) + 1;
let o = 0, s = 0;
values = new Float64Array(h * k * l);
for (let u = 0; u < h; u++) {
for (let v = 0; v < k; v++) {
for (let w = 0; w < l; w++) {
values[o++] = sourceValues[s];
s += nth;
}
}
}
}
const data = Tensor.create(space, Tensor.Data1(values));
const matrix = Mat4.fromTranslation(Mat4(), header.origin);
const basis = Mat4.fromBasis(Mat4(), header.basisX, header.basisY, header.basisZ);
Mat4.mul(matrix, matrix, basis);
return {
label: params?.label,
transform: { kind: 'matrix', matrix },
data,
dataStats: {
min: arrayMin(values),
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 };
}
}

View File

@@ -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(
@@ -34,17 +35,31 @@ function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<
const dimensions = Vec3.ofArray(normalizeOrder(info.dimensions.value(0)));
return {
cell,
fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)),
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
data,
dataStats: {
min: info.min_sampled.value(0),
max: info.max_sampled.value(0),
mean: info.mean_sampled.value(0),
sigma: info.sigma_sampled.value(0)
}
},
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 };
}
}

View File

@@ -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 }): 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);
@@ -32,17 +33,32 @@ function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3 }): Task<V
const data = Tensor.create(space, Tensor.Data1(values));
return {
cell,
fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)),
label: params?.label,
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
data,
dataStats: {
min: arrayMin(values),
max: arrayMax(values),
mean: arrayMean(values),
sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
}
},
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 };
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { DxFile } from '../../mol-io/reader/dx/parser';
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 () => {
const { header, values } = source;
const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
const data = Tensor.create(space, Tensor.Data1(values));
const matrix = Mat4.fromTranslation(Mat4(), header.min);
const basis = Mat4.fromScaling(Mat4(), header.h);
Mat4.mul(matrix, matrix, basis);
return {
label: params?.label,
transform: { kind: 'matrix', matrix },
data,
dataStats: {
min: arrayMin(values),
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 };
}
}

View File

@@ -16,6 +16,7 @@ import { shallowEqual } from '../mol-util';
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';
/** A Loci that includes every loci */
export const EveryLoci = { kind: 'every-loci' as 'every-loci' };
@@ -62,7 +63,7 @@ export function DataLoci<T = unknown, E = unknown>(tag: string, data: T, element
export { Loci };
type Loci = StructureElement.Loci | Structure.Loci | Bond.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci | ShapeGroup.Loci
type Loci = StructureElement.Loci | Structure.Loci | Bond.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci | ShapeGroup.Loci | Volume.Loci | Volume.Isosurface.Loci | Volume.Cell.Loci
namespace Loci {
export interface Bundle<L extends number> { loci: FiniteArray<Loci, L> }
@@ -98,6 +99,15 @@ namespace Loci {
if (ShapeGroup.isLoci(lociA) && ShapeGroup.isLoci(lociB)) {
return ShapeGroup.areLociEqual(lociA, lociB);
}
if (Volume.isLoci(lociA) && Volume.isLoci(lociB)) {
return Volume.areLociEqual(lociA, lociB);
}
if (Volume.Isosurface.isLoci(lociA) && Volume.Isosurface.isLoci(lociB)) {
return Volume.Isosurface.areLociEqual(lociA, lociB);
}
if (Volume.Cell.isLoci(lociA) && Volume.Cell.isLoci(lociB)) {
return Volume.Cell.areLociEqual(lociA, lociB);
}
return false;
}
@@ -114,6 +124,9 @@ namespace Loci {
if (Bond.isLoci(loci)) return Bond.isLociEmpty(loci);
if (Shape.isLoci(loci)) return Shape.isLociEmpty(loci);
if (ShapeGroup.isLoci(loci)) return ShapeGroup.isLociEmpty(loci);
if (Volume.isLoci(loci)) return Volume.isLociEmpty(loci);
if (Volume.Isosurface.isLoci(loci)) return Volume.Isosurface.isLociEmpty(loci);
if (Volume.Cell.isLoci(loci)) return Volume.Cell.isLociEmpty(loci);
return false;
}
@@ -147,6 +160,12 @@ namespace Loci {
return ShapeGroup.getBoundingSphere(loci, boundingSphere);
} else if (loci.kind === 'data-loci') {
return loci.getBoundingSphere(boundingSphere);
} else if (loci.kind === 'volume-loci') {
return Volume.getBoundingSphere(loci.volume, boundingSphere);
} else if (loci.kind === 'isosurface-loci') {
return Volume.Isosurface.getBoundingSphere(loci.volume, loci.isoValue, boundingSphere);
} else if (loci.kind === 'cell-loci') {
return Volume.Cell.getBoundingSphere(loci.volume, loci.indices, boundingSphere);
}
}
@@ -175,6 +194,15 @@ namespace Loci {
} else if (loci.kind === 'data-loci') {
// TODO maybe add loci.getPrincipalAxes()???
return void 0;
} else if (loci.kind === 'volume-loci') {
// TODO
return void 0;
} else if (loci.kind === 'isosurface-loci') {
// TODO
return void 0;
} else if (loci.kind === 'cell-loci') {
// TODO
return void 0;
}
}

View File

@@ -100,7 +100,7 @@ export namespace Shape {
export function Loci(shape: Shape): Loci { return { kind: 'shape-loci', shape }; }
export function isLoci(x: any): x is Loci { return !!x && x.kind === 'shape-loci'; }
export function areLociEqual(a: Loci, b: Loci) { return a.shape === b.shape; }
export function isLociEmpty(loci: Loci) { return loci.shape.groupCount === 0 ? true : false; }
export function isLociEmpty(loci: Loci) { return loci.shape.groupCount === 0; }
}
export namespace ShapeGroup {

View File

@@ -6,18 +6,21 @@
import { SymmetryOperator } from '../../../../mol-math/geometry';
import { CifExportContext } from '../mmcif';
import { StructureElement, StructureProperties as P } from '../../structure';
import { StructureElement, StructureProperties as P, CifExportCategoryInfo } from '../../structure';
import Unit from '../../structure/unit';
import { Segmentation } from '../../../../mol-data/int';
import { CifWriter } from '../../../../mol-io/writer/cif';
import { Column } from '../../../../mol-data/db';
export function atom_site_operator_mapping(encoder: CifWriter.Encoder, ctx: CifExportContext) {
export function atom_site_operator_mapping(ctx: CifExportContext): CifExportCategoryInfo | undefined {
const entries = getEntries(ctx);
if (entries.length === 0) return;
encoder.writeCategory(Category, entries, { ignoreFilter: true });
return [Category, entries, { ignoreFilter: true }];
}
export const AtomSiteOperatorMappingCategoryName = 'molstar_atom_site_operator_mapping';
export const AtomSiteOperatorMappingSchema = {
molstar_atom_site_operator_mapping: {
label_asym_id: Column.Schema.Str(),

View File

@@ -14,6 +14,7 @@ import { sortArray } from '../../../../mol-data/util';
import { CifWriter } from '../../../../mol-io/writer/cif';
import { CifExportContext } from '../mmcif';
import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
import { CifCategory, CifField, getCifFieldType } from '../../../../mol-io/reader/cif';
export function getModelMmCifCategory<K extends keyof mmCIF_Schema>(model: Model, name: K): mmCIF_Database[K] | undefined {
if (!MmcifFormat.is(model.sourceData)) return;
@@ -58,4 +59,41 @@ export function copy_mmCif_category(name: keyof mmCIF_Schema, condition?: (struc
return CifWriter.Category.ofTable(table);
}
};
}
export function copy_source_mmCifCategory(encoder: CifWriter.Encoder, ctx: CifExportContext, category: CifCategory): CifWriter.Category<CifExportContext> | undefined {
if (!MmcifFormat.is(ctx.firstModel.sourceData)) return;
const fs = CifWriter.fields<number, undefined>();
if (encoder.isBinary) {
for (const f of category.fieldNames) {
// TODO: this could be optimized
const field = classifyField(f, category.getField(f)!);
fs.add(field);
}
} else {
for (const f of category.fieldNames) {
const field = category.getField(f)!;
fs.str(f, row => field.str(row));
}
}
const fields = fs.getFields();
return {
name: category.name,
instance() {
return { fields, source: [{ data: void 0, rowCount: category.rowCount }] };
}
};
}
function classifyField(name: string, field: CifField): CifWriter.Field {
const type = getCifFieldType(field);
if (type['@type'] === 'str') {
return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
} else if (type['@type'] === 'float') {
return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, typedArray: Float64Array });
} else {
return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, typedArray: Int32Array });
}
}

View File

@@ -13,10 +13,11 @@ import CifCategory = CifWriter.Category
import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure';
import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './categories/misc';
import { Model } from '../model';
import { getUniqueEntityIndicesFromStructures, copy_mmCif_category } from './categories/utils';
import { getUniqueEntityIndicesFromStructures, copy_mmCif_category, copy_source_mmCifCategory } from './categories/utils';
import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
import { CustomPropertyDescriptor } from '../common/custom-property';
import { atom_site_operator_mapping } from './categories/atom_site_operator_mapping';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
export interface CifExportContext {
structures: Structure[],
@@ -24,6 +25,10 @@ export interface CifExportContext {
cache: any
}
export type CifExportCategoryInfo =
| [CifWriter.Category, any /** context */, CifWriter.Encoder.WriteCategoryOptions]
| [CifWriter.Category, any /** context */]
export namespace CifExportContext {
export function create(structures: Structure | Structure[]): CifExportContext {
const structureArray = Array.isArray(structures) ? structures : [structures];
@@ -99,8 +104,8 @@ export const mmCIF_Export_Filters = {
}
};
function encodeCustomProp(customProp: CustomPropertyDescriptor, ctx: CifExportContext, encoder: CifWriter.Encoder, params: encode_mmCIF_categories_Params) {
if (!customProp.cifExport || customProp.cifExport.categories.length === 0) return;
function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifExportContext, params?: encode_mmCIF_categories_Params): CifExportCategoryInfo[] {
if (!customProp.cifExport || customProp.cifExport.categories.length === 0) return [];
const prefix = customProp.cifExport.prefix;
const cats = customProp.cifExport.categories;
@@ -114,14 +119,21 @@ function encodeCustomProp(customProp: CustomPropertyDescriptor, ctx: CifExportCo
ctx.cache[propId + '__ctx'] = propCtx;
}
}
const ret: CifExportCategoryInfo[] = [];
for (const cat of cats) {
if (params.skipCategoryNames && params.skipCategoryNames.has(cat.name)) continue;
if (params?.skipCategoryNames?.has(cat.name)) continue;
if (cat.name.indexOf(prefix) !== 0) throw new Error(`Custom category '${cat.name}' name must start with prefix '${prefix}.'`);
encoder.writeCategory(cat, propCtx);
ret.push([cat, propCtx]);
}
return ret;
}
type encode_mmCIF_categories_Params = { skipCategoryNames?: Set<string>, exportCtx?: CifExportContext }
type encode_mmCIF_categories_Params = {
skipCategoryNames?: Set<string>,
exportCtx?: CifExportContext,
copyAllCategories?: boolean
}
/** Doesn't start a data block */
export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures: Structure | Structure[], params?: encode_mmCIF_categories_Params) {
@@ -129,30 +141,94 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
const models = first.models;
if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.';
const _params = params || { };
const ctx: CifExportContext = params && params.exportCtx ? params.exportCtx : CifExportContext.create(structures);
const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures);
if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) {
encode_mmCIF_categories_copyAll(encoder, ctx);
} else {
encode_mmCIF_categories_default(encoder, ctx, params);
}
}
function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExportContext, params?: encode_mmCIF_categories_Params) {
for (const cat of Categories) {
if (_params.skipCategoryNames && _params.skipCategoryNames.has(cat.name)) continue;
if (params?.skipCategoryNames && params?.skipCategoryNames.has(cat.name)) continue;
encoder.writeCategory(cat, ctx);
}
if ((!_params.skipCategoryNames || !_params.skipCategoryNames.has('atom_site')) && encoder.isCategoryIncluded('atom_site')) {
atom_site_operator_mapping(encoder, ctx);
if (!params?.skipCategoryNames?.has('atom_site') && encoder.isCategoryIncluded('atom_site')) {
const info = atom_site_operator_mapping(ctx);
if (info) encoder.writeCategory(info[0], info[1], info[2]);
}
for (const customProp of models[0].customProperties.all) {
encodeCustomProp(customProp, ctx, encoder, _params);
const _params = params || { };
for (const customProp of ctx.firstModel.customProperties.all) {
for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) {
encoder.writeCategory(cat, propCtx);
}
}
const structureCustomProps = new Set<CustomPropertyDescriptor>();
for (const s of ctx.structures) {
if (!s.hasCustomProperties) continue;
for (const p of s.customPropertyDescriptors.all) structureCustomProps.add(p);
for (const customProp of s.customPropertyDescriptors.all) {
for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) {
encoder.writeCategory(cat, propCtx);
}
}
}
structureCustomProps.forEach(customProp => encodeCustomProp(customProp, ctx, encoder, _params));
}
function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext) {
const providedCategories = new Map<string, CifExportCategoryInfo>();
for (const cat of Categories) {
providedCategories.set(cat.name, [cat, ctx]);
}
const mapping = atom_site_operator_mapping(ctx);
if (mapping) providedCategories.set(mapping[0].name, mapping);
for (const customProp of ctx.firstModel.customProperties.all) {
for (const info of getCustomPropCategories(customProp, ctx)) {
providedCategories.set(info[0].name, info);
}
}
for (const s of ctx.structures) {
if (!s.hasCustomProperties) continue;
for (const customProp of s.customPropertyDescriptors.all) {
for (const info of getCustomPropCategories(customProp, ctx)) {
providedCategories.set(info[0].name, info);
}
}
}
const handled = new Set<string>();
const data = (ctx.firstModel.sourceData as MmcifFormat).data;
for (const catName of data.frame.categoryNames) {
handled.add(catName);
if (providedCategories.has(catName)) {
const info = providedCategories.get(catName)!;
encoder.writeCategory(info[0], info[1], info[2]);
} else {
if ((data.db as any)[catName]) {
const cat = copy_mmCif_category(catName as any);
encoder.writeCategory(cat, ctx);
} else {
const cat = copy_source_mmCifCategory(encoder, ctx, data.frame.categories[catName]);
if (cat) encoder.writeCategory(cat);
}
}
}
providedCategories.forEach((info, name) => {
if (!handled.has(name)) encoder.writeCategory(info[0], info[1], info[2]);
});
}
function to_mmCIF(name: string, structure: Structure, asBinary = false) {
const enc = CifWriter.createEncoder({ binary: asBinary });
enc.startDataBlock(name);

View File

@@ -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';
@@ -25,6 +25,7 @@ import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
import { ChainIndex } from './indexing';
import { SymmetryOperator } from '../../../mol-math/geometry';
import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry';
import { Column } from '../../../mol-data/db';
/**
* Interface to the "source data" of the molecule.
@@ -143,16 +144,19 @@ export namespace Model {
//
export function isFromPdbArchive(model: Model) {
export function isFromPdbArchive(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
return (
db.database_2.database_id.isDefined ||
model.entryId.length === 4
// 4 character PDB id
model.entryId.match(/^[1-9][a-z0-9]{3,3}$/i) !== null ||
// long PDB id
model.entryId.match(/^pdb_[0-9]{4,4}[1-9][a-z0-9]{3,3}$/i) !== null
);
}
export function hasSecondaryStructure(model: Model) {
export function hasSecondaryStructure(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
return (
@@ -163,7 +167,7 @@ export namespace Model {
const tmpAngles90 = Vec3.create(1.5707963, 1.5707963, 1.5707963); // in radians
const tmpLengths1 = Vec3.create(1, 1, 1);
export function hasCrystalSymmetry(model: Model) {
export function hasCrystalSymmetry(model: Model): boolean {
const spacegroup = ModelSymmetry.Provider.get(model)?.spacegroup;
return !!spacegroup && !(
spacegroup.num === 1 &&
@@ -172,7 +176,7 @@ export namespace Model {
);
}
export function isFromXray(model: Model) {
export function isFromXray(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.exptl.method.rowCount; i++) {
@@ -182,7 +186,7 @@ export namespace Model {
return false;
}
export function isFromEm(model: Model) {
export function isFromEm(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.exptl.method.rowCount; i++) {
@@ -192,7 +196,7 @@ export namespace Model {
return false;
}
export function isFromNmr(model: Model) {
export function isFromNmr(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.exptl.method.rowCount; i++) {
@@ -202,7 +206,7 @@ export namespace Model {
return false;
}
export function hasXrayMap(model: Model) {
export function hasXrayMap(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
// Check exprimental method to exclude models solved with
// 'ELECTRON CRYSTALLOGRAPHY' which also have structure factors
@@ -217,7 +221,7 @@ export namespace Model {
* like 6TEK which are solved with 'X-RAY DIFFRACTION' but have an related
* EMDB entry of type 'other EM volume'.
*/
export function hasEmMap(model: Model) {
export function hasEmMap(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
const { db_name, content_type } = db.pdbx_database_related;
@@ -229,7 +233,26 @@ export namespace Model {
return false;
}
export function hasDensityMap(model: Model) {
export function hasDensityMap(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
return hasXrayMap(model) || hasEmMap(model);
}
export function probablyHasDensityMap(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
return hasDensityMap(model) || (
// check if from pdb archive but missing relevant meta data
isFromPdbArchive(model) && (
!db.exptl.method.isDefined ||
(isFromXray(model) && (
!db.pdbx_database_status.status_code_sf.isDefined ||
db.pdbx_database_status.status_code_sf.valueKind(0) === Column.ValueKind.Unknown
)) ||
(isFromEm(model) && (
!db.pdbx_database_related.db_name.isDefined
))
)
);
}
}

View File

@@ -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); }

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