mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
243 Commits
v0.7.0-dev
...
v1.0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0b98f70f0 | ||
|
|
297b9bd3ff | ||
|
|
013a59857d | ||
|
|
0b14381255 | ||
|
|
e1e0b0f2da | ||
|
|
2142290300 | ||
|
|
aeb7c7033d | ||
|
|
0f8540e7fc | ||
|
|
f14b57fe30 | ||
|
|
abfb1d5992 | ||
|
|
35c53b27fe | ||
|
|
a7f144e810 | ||
|
|
f4cb3aeed7 | ||
|
|
bceb044552 | ||
|
|
5e41e959f8 | ||
|
|
c95d54f9cd | ||
|
|
8149a25ad4 | ||
|
|
d500393501 | ||
|
|
9d2fa3e749 | ||
|
|
76a5ce8f14 | ||
|
|
d121ed8b6c | ||
|
|
3ab4458cb2 | ||
|
|
a3d101cdf9 | ||
|
|
7255e08ecf | ||
|
|
b1bdb8e66b | ||
|
|
49c8c7f396 | ||
|
|
d3b4280589 | ||
|
|
bb9acaaa9c | ||
|
|
fc10b9bf7b | ||
|
|
b2fdcba674 | ||
|
|
0d01948ba9 | ||
|
|
c4370670cb | ||
|
|
1b19136c18 | ||
|
|
217e983da8 | ||
|
|
57338bdad1 | ||
|
|
0e84bf9513 | ||
|
|
0be28cacdf | ||
|
|
017c608439 | ||
|
|
20ee9496e3 | ||
|
|
6fd81d0961 | ||
|
|
823a68f9bf | ||
|
|
deab18e805 | ||
|
|
19016b6730 | ||
|
|
4319ae251c | ||
|
|
e5920e29b4 | ||
|
|
c376ddfc9d | ||
|
|
8fe2d3f724 | ||
|
|
4d7a128528 | ||
|
|
663ec9695e | ||
|
|
dfaa4dacdb | ||
|
|
f7adb8b589 | ||
|
|
cb6b1bf19d | ||
|
|
27a4e1d7d9 | ||
|
|
0cba88ad8c | ||
|
|
e535c4efa8 | ||
|
|
31f58ee110 | ||
|
|
feb167dcf8 | ||
|
|
1b53ea846b | ||
|
|
88b9be5fd1 | ||
|
|
89486ea9e2 | ||
|
|
86c09ead98 | ||
|
|
1f60d887a8 | ||
|
|
a672115505 | ||
|
|
8f54ea137d | ||
|
|
4171008c3f | ||
|
|
3a9c3780ac | ||
|
|
fe55f33bd1 | ||
|
|
71bc88c041 | ||
|
|
a5aadfef0e | ||
|
|
0b368ef804 | ||
|
|
f398993d33 | ||
|
|
b6f59ca9c3 | ||
|
|
c857c17bb4 | ||
|
|
3415fe0847 | ||
|
|
1569958a29 | ||
|
|
3543faa0c2 | ||
|
|
251dbf3877 | ||
|
|
32d35efef0 | ||
|
|
8b6428a61d | ||
|
|
dda43370cf | ||
|
|
92c1e979c0 | ||
|
|
ad38a33943 | ||
|
|
88c276a4c7 | ||
|
|
0a3d19235d | ||
|
|
0d90fd1f06 | ||
|
|
02d3274e83 | ||
|
|
2531af2b94 | ||
|
|
850328be4e | ||
|
|
f8ce9cbb65 | ||
|
|
2af9d1cabf | ||
|
|
e8d1737d40 | ||
|
|
0328e93518 | ||
|
|
8a4ab9bdb9 | ||
|
|
410cdb193d | ||
|
|
a278337b4c | ||
|
|
b1308de0b9 | ||
|
|
9705078970 | ||
|
|
b1ca98e945 | ||
|
|
35054eaca9 | ||
|
|
2747c743c9 | ||
|
|
031d08a8d4 | ||
|
|
7cc6c4a9c8 | ||
|
|
ff27098514 | ||
|
|
545cd65066 | ||
|
|
84bfc6e7a9 | ||
|
|
2f71c4c5e4 | ||
|
|
1448f7aeb6 | ||
|
|
79d66a5cfc | ||
|
|
2ec19ac04c | ||
|
|
f62a6d4512 | ||
|
|
4fbcee3953 | ||
|
|
12bb283b97 | ||
|
|
13d776c7cb | ||
|
|
f45b48c6e1 | ||
|
|
ff14c94a90 | ||
|
|
0a0ef35b74 | ||
|
|
e3dc10c085 | ||
|
|
46113bf3d4 | ||
|
|
0f3ef61f7d | ||
|
|
86aae08257 | ||
|
|
06bf2c39a1 | ||
|
|
66a23bc2a2 | ||
|
|
51e86f1e43 | ||
|
|
78c70b3f5b | ||
|
|
8f52ffe061 | ||
|
|
e95b91ab84 | ||
|
|
f4dbd66496 | ||
|
|
5895df0499 | ||
|
|
6d0d88f3be | ||
|
|
7e71428cc3 | ||
|
|
2e215440f7 | ||
|
|
c04fa56c6c | ||
|
|
6c70b5e38f | ||
|
|
ad9160a4a3 | ||
|
|
c747d3928e | ||
|
|
68e8d67054 | ||
|
|
9e8fc76d28 | ||
|
|
d8970305de | ||
|
|
824675a658 | ||
|
|
090ad613cc | ||
|
|
ea35e09c96 | ||
|
|
d8b9a1a560 | ||
|
|
c259f58e63 | ||
|
|
9d4c2a1147 | ||
|
|
f13c3fe38b | ||
|
|
60409df145 | ||
|
|
1d7321cd6f | ||
|
|
eb68ccbf6b | ||
|
|
b1ece44c49 | ||
|
|
37ae274fb6 | ||
|
|
c900045fcd | ||
|
|
50d95ccf6a | ||
|
|
c9171444eb | ||
|
|
56ea62af71 | ||
|
|
9e81626928 | ||
|
|
84fda6e35d | ||
|
|
0f758cf554 | ||
|
|
a6605052db | ||
|
|
8514175da2 | ||
|
|
6a49427fc0 | ||
|
|
7c18e5eb86 | ||
|
|
2a7d258715 | ||
|
|
54fb9beeee | ||
|
|
27ebbc50d5 | ||
|
|
2a1b6e52b2 | ||
|
|
3110e82d92 | ||
|
|
4be999ce32 | ||
|
|
f0d7a4ed2a | ||
|
|
2dacfcb485 | ||
|
|
6218cc5371 | ||
|
|
b4808f2909 | ||
|
|
1a2e9eaa84 | ||
|
|
056ce42097 | ||
|
|
b14b5ca626 | ||
|
|
ffbaa944f2 | ||
|
|
e2ba96174a | ||
|
|
8c5d99bb54 | ||
|
|
b18b3be070 | ||
|
|
2e69b7c419 | ||
|
|
5007f5fb72 | ||
|
|
6fe83a9a70 | ||
|
|
20af084127 | ||
|
|
d6501170e6 | ||
|
|
5f33364514 | ||
|
|
7924c008fa | ||
|
|
2d2a53f28e | ||
|
|
1f7ffabef9 | ||
|
|
16d5c07224 | ||
|
|
2392bfb579 | ||
|
|
b4036f576c | ||
|
|
7e3cca5780 | ||
|
|
690d6812dc | ||
|
|
a44aa02f13 | ||
|
|
65ddd6d68a | ||
|
|
754025b3b1 | ||
|
|
f0649c5aa3 | ||
|
|
6df045211c | ||
|
|
0b9371527e | ||
|
|
8a00540de0 | ||
|
|
0d78905686 | ||
|
|
6edab203c2 | ||
|
|
0abfdb5ee3 | ||
|
|
88369158c9 | ||
|
|
8926575283 | ||
|
|
15b0288ce4 | ||
|
|
28853ec19d | ||
|
|
e3480a076a | ||
|
|
abf6452124 | ||
|
|
2cdd811dd3 | ||
|
|
be6fea39bf | ||
|
|
ed1bc2cd07 | ||
|
|
28d3d5861a | ||
|
|
95d3ef491f | ||
|
|
5c37ddfc6d | ||
|
|
4d9e2d9c91 | ||
|
|
0b1c18913d | ||
|
|
7aee2d805d | ||
|
|
06f03f399a | ||
|
|
c8c2355d3e | ||
|
|
3d1366024d | ||
|
|
f6964d2a66 | ||
|
|
de60f70af5 | ||
|
|
8f2e619162 | ||
|
|
fbcef01c55 | ||
|
|
641e0639d4 | ||
|
|
5048573976 | ||
|
|
78e4d8536d | ||
|
|
a01d088205 | ||
|
|
4b1d1a045d | ||
|
|
e2857d00b4 | ||
|
|
a55a71d31a | ||
|
|
acf793f112 | ||
|
|
2d58ea28ea | ||
|
|
b21de78eb5 | ||
|
|
376d4b4ee1 | ||
|
|
e39304c7cf | ||
|
|
7cba9cda0c | ||
|
|
04c690e8f9 | ||
|
|
170d0fbc9d | ||
|
|
40d632a7b1 | ||
|
|
2e754d23f4 | ||
|
|
5639a4b37c | ||
|
|
8c959f8a60 |
@@ -12,7 +12,7 @@
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-types": "warn",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/class-name-casing": "off",
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
|
||||
1
.npmignore
Normal file
1
.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
tsconfig.servers.buildinfo
|
||||
@@ -14,6 +14,5 @@ before_install:
|
||||
node_js:
|
||||
- "12"
|
||||
- "10"
|
||||
- "8"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start
|
||||
4704
package-lock.json
generated
4704
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.7.0-dev.2",
|
||||
"version": "1.0.5",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -35,11 +35,12 @@
|
||||
"volume-server-test": "node lib/servers/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/servers/servers/plugin-state/index.js",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
"version": "npm run build",
|
||||
"postversion": "git push && git push --tags"
|
||||
},
|
||||
"files": [
|
||||
"lib/"
|
||||
"lib/",
|
||||
"build/viewer/"
|
||||
],
|
||||
"bin": {
|
||||
"cif2bcif": "lib/apps/cif2bcif/index.js",
|
||||
@@ -78,71 +79,68 @@
|
||||
"contributors": [
|
||||
"Alexander Rose <alexander.rose@weirdbyte.de>",
|
||||
"David Sehnal <david.sehnal@gmail.com>",
|
||||
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>"
|
||||
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>",
|
||||
"Ludovic Autin <autin@scripps.edu>"
|
||||
],
|
||||
"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.14.0",
|
||||
"@graphql-codegen/cli": "^1.14.0",
|
||||
"@graphql-codegen/time": "^1.14.0",
|
||||
"@graphql-codegen/typescript": "^1.14.0",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.14.0",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.14.0",
|
||||
"@graphql-codegen/typescript-operations": "^1.14.0",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^2.28.0",
|
||||
"@typescript-eslint/parser": "^2.28.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.0.0",
|
||||
"@typescript-eslint/parser": "^3.0.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"concurrently": "^5.2.0",
|
||||
"cpx2": "^2.0.0",
|
||||
"css-loader": "^3.5.2",
|
||||
"eslint": "^6.8.0",
|
||||
"css-loader": "^3.5.3",
|
||||
"eslint": "^7.0.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-raw-loader": "^1.0.1",
|
||||
"http-server": "^0.12.3",
|
||||
"jest": "^26.0.1",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"node-sass": "^4.14.1",
|
||||
"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",
|
||||
"ts-jest": "^25.4.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"simple-git": "^2.5.0",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-jest": "^26.0.0",
|
||||
"typescript": "^3.9.3",
|
||||
"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/icons": "^4.9.1",
|
||||
"@types/argparse": "^1.0.38",
|
||||
"@types/benchmark": "^1.0.31",
|
||||
"@types/benchmark": "^1.0.33",
|
||||
"@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/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.6",
|
||||
"@types/jest": "^25.2.3",
|
||||
"@types/node": "^14.0.5",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/swagger-ui-dist": "3.0.5",
|
||||
"argparse": "^1.0.10",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"immer": "^6.0.3",
|
||||
"immer": "^6.0.6",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"rxjs": "^6.5.5",
|
||||
"swagger-ui-dist": "^3.25.0",
|
||||
"tslib": "^1.11.1",
|
||||
"swagger-ui-dist": "^3.25.4",
|
||||
"tslib": "^2.0.0",
|
||||
"util.promisify": "^1.0.1",
|
||||
"xhr2": "^0.2.0"
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ export function printSequence(model: Model) {
|
||||
for (const key of Object.keys(byEntityKey)) {
|
||||
const { sequence, entityId } = byEntityKey[+key];
|
||||
const { seqId, compId } = sequence;
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)}, ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${Sequence.getSequenceString(sequence)}`);
|
||||
}
|
||||
console.log();
|
||||
|
||||
@@ -8,38 +8,36 @@ import * as fs from 'fs';
|
||||
import * as argparse from 'argparse';
|
||||
import * as util from 'util';
|
||||
|
||||
import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { downloadCif } from './helpers';
|
||||
import { CIF } from '../../mol-io/reader/cif';
|
||||
import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
|
||||
import { Table } from '../../mol-data/db';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { Task } from '../../mol-task';
|
||||
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
|
||||
import { volumeFromDensityServerData, DscifFormat } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
require('util.promisify').shim();
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
|
||||
type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
|
||||
|
||||
async function getVolume(url: string): Promise<Volume> {
|
||||
const cif = await downloadCif(url, true);
|
||||
const data = CIF.schema.densityServer(cif.blocks[1]);
|
||||
return { source: data, volume: await volumeFromDensityServerData(data).run() };
|
||||
return await volumeFromDensityServerData(data).run();
|
||||
}
|
||||
|
||||
function print(data: Volume) {
|
||||
const { volume_data_3d_info } = data.source;
|
||||
function print(volume: Volume) {
|
||||
if (!DscifFormat.is(volume.sourceData)) return;
|
||||
const { volume_data_3d_info } = volume.sourceData.data;
|
||||
const row = Table.getRow(volume_data_3d_info, 0);
|
||||
console.log(row);
|
||||
if (data.volume.transform) console.log(data.volume.transform);
|
||||
console.log(data.volume.dataStats);
|
||||
console.log(volume.grid.transform);
|
||||
console.log(volume.grid.stats);
|
||||
}
|
||||
|
||||
async function doMesh(data: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
async function doMesh(volume: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
|
||||
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
|
||||
|
||||
// Export the mesh in OBJ format.
|
||||
|
||||
63
src/apps/viewer/embedded.html
Normal file
63
src/apps/viewer/embedded.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>Embedded Mol* Viewer</title>
|
||||
<style>
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 100px;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./molstar.js"></script>
|
||||
<script type="text/javascript">
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
layoutIsExpanded: false,
|
||||
layoutShowControls: false,
|
||||
layoutShowRemoteState: false,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: false,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: true,
|
||||
viewportShowSelectionMode: false,
|
||||
viewportShowAnimation: false,
|
||||
|
||||
pdbProvider: 'rcsb',
|
||||
emdbProvider: 'rcsb',
|
||||
});
|
||||
viewer.loadPdb('7bv2');
|
||||
viewer.loadEmdb('EMD-30210');
|
||||
|
||||
// TODO add Volume.customProperty and load suggested isoValue via custom property
|
||||
var sub = viewer.plugin.managers.volume.hierarchy.behaviors.selection.subscribe(function (value) {
|
||||
if (value.volume?.representations[0]) {
|
||||
var ref = value.volume.representations[0].cell;
|
||||
var tree = viewer.plugin.state.data.build().to(ref).update({
|
||||
type: {
|
||||
name: 'isosurface',
|
||||
params: {
|
||||
isoValue: {
|
||||
kind: 'relative',
|
||||
relativeValue: 6
|
||||
}
|
||||
}
|
||||
},
|
||||
colorTheme: ref.transform.params?.colorTheme
|
||||
});
|
||||
viewer.plugin.runTask(viewer.plugin.state.data.updateTree(tree));
|
||||
if (typeof sub !== 'undefined') sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -34,10 +34,50 @@
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
<script type="text/javascript" src="./molstar.js"></script>
|
||||
<script type="text/javascript">
|
||||
function getParam(name, regex) {
|
||||
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
|
||||
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
|
||||
if (debugMode) molstar.setDebugMode(debugMode);
|
||||
|
||||
var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
|
||||
var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
layoutShowControls: !hideControls,
|
||||
viewportShowExpand: false,
|
||||
pdbProvider: pdbProvider || 'pdbe',
|
||||
emdbProvider: emdbProvider || 'pdbe',
|
||||
});
|
||||
|
||||
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
|
||||
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
|
||||
|
||||
var snapshotUrl = getParam('snapshot-url', '[^&]+').trim();
|
||||
var snapshotUrlType = getParam('snapshot-url-type', '[^&]+').toLowerCase().trim() || 'molj';
|
||||
if (snapshotUrl && snapshotUrlType) viewer.loadSnapshotFromUrl(snapshotUrl, snapshotUrlType);
|
||||
|
||||
var structureUrl = getParam('structure-url', '[^&]+').trim();
|
||||
var structureUrlFormat = getParam('structure-url-format', '[a-z]+').toLowerCase().trim();
|
||||
var structureUrlIsBinary = getParam('structure-url-is-binary', '[^&]+').trim() === '1';
|
||||
if (structureUrl) viewer.loadStructureFromUrl(structureUrl, structureUrlFormat, structureUrlIsBinary);
|
||||
|
||||
var pdb = getParam('pdb', '[^&]+').trim();
|
||||
if (pdb) viewer.loadPdb(pdb);
|
||||
|
||||
var pdbDev = getParam('pdb-dev', '[^&]+').trim();
|
||||
if (pdbDev) viewer.loadPdbDev(pdbDev);
|
||||
|
||||
var emdb = getParam('emdb', '[^&]+').trim();
|
||||
if (emdb) viewer.loadEmdb(emdb);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,84 +8,117 @@
|
||||
import '../../mol-util/polyfill';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html';
|
||||
import './embedded.html';
|
||||
import './favicon.ico';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { CellPack } from '../../extensions/cellpack';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { ObjectKeys } from '../../mol-util/type-helpers';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
function getParam(name: string, regex: string): string {
|
||||
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
|
||||
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
|
||||
|
||||
const hideControls = getParam('hide-controls', `[^&]+`) === '1';
|
||||
const Extensions = {
|
||||
'cellpack': PluginSpec.Behavior(CellPack),
|
||||
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
|
||||
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
|
||||
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport)
|
||||
};
|
||||
|
||||
function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [...DefaultPluginSpec.actions],
|
||||
behaviors: [
|
||||
...DefaultPluginSpec.behaviors,
|
||||
PluginSpec.Behavior(CellPack),
|
||||
PluginSpec.Behavior(PDBeStructureQualityReport),
|
||||
PluginSpec.Behavior(RCSBAssemblySymmetry),
|
||||
PluginSpec.Behavior(RCSBValidationReport),
|
||||
],
|
||||
animations: [...DefaultPluginSpec.animations || []],
|
||||
customParamEditors: DefaultPluginSpec.customParamEditors,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: true,
|
||||
showControls: !hideControls
|
||||
const DefaultViewerOptions = {
|
||||
extensions: ObjectKeys(Extensions),
|
||||
layoutIsExpanded: true,
|
||||
layoutShowControls: true,
|
||||
layoutShowRemoteState: true,
|
||||
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
|
||||
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
|
||||
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
|
||||
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
|
||||
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
|
||||
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
|
||||
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
|
||||
};
|
||||
type ViewerOptions = typeof DefaultViewerOptions;
|
||||
|
||||
export class Viewer {
|
||||
plugin: PluginContext
|
||||
|
||||
constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
|
||||
const o = { ...DefaultViewerOptions, ...options };
|
||||
|
||||
const spec: PluginSpec = {
|
||||
actions: [...DefaultPluginSpec.actions],
|
||||
behaviors: [
|
||||
...DefaultPluginSpec.behaviors,
|
||||
...o.extensions.map(e => Extensions[e]),
|
||||
],
|
||||
animations: [...DefaultPluginSpec.animations || []],
|
||||
customParamEditors: DefaultPluginSpec.customParamEditors,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: o.layoutIsExpanded,
|
||||
showControls: o.layoutShowControls,
|
||||
controlsDisplay: o.layoutControlsDisplay,
|
||||
},
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls,
|
||||
top: o.layoutShowSequence ? undefined : 'none',
|
||||
bottom: o.layoutShowLog ? undefined : 'none',
|
||||
left: o.layoutShowLeftPanel ? undefined : 'none',
|
||||
}
|
||||
},
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
|
||||
}
|
||||
},
|
||||
config: DefaultPluginSpec.config
|
||||
};
|
||||
spec.config?.set(PluginConfig.Viewport.ShowExpand, false);
|
||||
const plugin = createPlugin(document.getElementById('app')!, spec);
|
||||
trySetSnapshot(plugin);
|
||||
tryLoadFromUrl(plugin);
|
||||
}
|
||||
components: {
|
||||
...DefaultPluginSpec.components,
|
||||
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
|
||||
},
|
||||
config: [
|
||||
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
|
||||
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
|
||||
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
|
||||
[PluginConfig.State.DefaultServer, o.pluginStateServer],
|
||||
[PluginConfig.State.CurrentServer, o.pluginStateServer],
|
||||
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
|
||||
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
|
||||
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
|
||||
]
|
||||
};
|
||||
|
||||
async function trySetSnapshot(ctx: PluginContext) {
|
||||
try {
|
||||
const snapshotUrl = getParam('snapshot-url', `[^&]+`);
|
||||
const snapshotId = getParam('snapshot-id', `[^&]+`);
|
||||
if (!snapshotUrl && !snapshotId) return;
|
||||
// TODO parametrize the server
|
||||
const url = snapshotId
|
||||
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
|
||||
: snapshotUrl;
|
||||
await PluginCommands.State.Snapshots.Fetch(ctx, { url });
|
||||
} catch (e) {
|
||||
ctx.log.error('Failed to load snapshot.');
|
||||
console.warn('Failed to load snapshot', e);
|
||||
const element = typeof elementOrId === 'string'
|
||||
? document.getElementById(elementOrId)
|
||||
: elementOrId;
|
||||
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
|
||||
this.plugin = createPlugin(element, spec);
|
||||
}
|
||||
}
|
||||
|
||||
async function tryLoadFromUrl(ctx: PluginContext) {
|
||||
const url = getParam('loadFromURL', '[^&]+').trim();
|
||||
try {
|
||||
if (!url) return;
|
||||
setRemoteSnapshot(id: string) {
|
||||
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
|
||||
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
|
||||
}
|
||||
|
||||
let format = 'cif', isBinary = false;
|
||||
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
|
||||
case 'pdb': format = 'pdb'; break;
|
||||
case 'mmbcif': isBinary = true; break;
|
||||
}
|
||||
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
|
||||
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
|
||||
}
|
||||
|
||||
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
|
||||
|
||||
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
|
||||
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
@@ -96,10 +129,63 @@ async function tryLoadFromUrl(ctx: PluginContext) {
|
||||
}
|
||||
}
|
||||
}));
|
||||
} catch (e) {
|
||||
ctx.log.error(`Failed to load from URL (${url})`);
|
||||
console.warn(`Failed to load from URL (${url})`, e);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
|
||||
const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
|
||||
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
|
||||
}
|
||||
|
||||
loadPdb(pdb: string) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'pdb' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: pdb,
|
||||
server: {
|
||||
name: provider,
|
||||
params: PdbDownloadProvider[provider].defaultValue as any
|
||||
}
|
||||
},
|
||||
options: params.source.params.options,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
loadPdbDev(pdbDev: string) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'pdb-dev' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: pdbDev,
|
||||
encoding: 'bcif',
|
||||
},
|
||||
options: params.source.params.options,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
loadEmdb(emdb: string) {
|
||||
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
|
||||
source: {
|
||||
name: 'pdb-emd-ds' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: emdb,
|
||||
server: provider,
|
||||
},
|
||||
detail: 3,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -55,13 +55,13 @@
|
||||
</select>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
<script>
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
|
||||
var pdbId = '1grm', assemblyId= '1';
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
|
||||
var format = 'mmcif';
|
||||
|
||||
|
||||
$('url').value = url;
|
||||
$('url').onchange = function (e) { url = e.target.value; }
|
||||
$('assemblyId').value = assemblyId;
|
||||
@@ -69,15 +69,8 @@
|
||||
$('format').value = format;
|
||||
$('format').onchange = function (e) { format = e.target.value; }
|
||||
|
||||
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
|
||||
// var format = 'pdb';
|
||||
// var assemblyId = 'deposited';
|
||||
|
||||
BasicMolStarWrapper.init('app' /** or document.getElementById('app') */);
|
||||
BasicMolStarWrapper.setBackground(0xffffff);
|
||||
// BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId });
|
||||
// BasicMolStarWrapper.toggleSpin();
|
||||
|
||||
|
||||
addControl('Load Asym Unit', () => BasicMolStarWrapper.load({ url: url, format: format }));
|
||||
addControl('Load Assembly', () => BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
|
||||
@@ -86,7 +79,7 @@
|
||||
|
||||
addHeader('Camera');
|
||||
addControl('Toggle Spin', () => BasicMolStarWrapper.toggleSpin());
|
||||
|
||||
|
||||
addSeparator();
|
||||
|
||||
addHeader('Animation');
|
||||
@@ -115,7 +108,7 @@
|
||||
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
|
||||
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
|
||||
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
|
||||
|
||||
|
||||
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
|
||||
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());
|
||||
|
||||
|
||||
@@ -56,7 +56,13 @@ class BasicWrapper {
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
|
||||
|
||||
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
|
||||
structure: assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } },
|
||||
structure: assemblyId ? {
|
||||
name: 'assembly',
|
||||
params: { id: assemblyId }
|
||||
} : {
|
||||
name: 'model',
|
||||
params: { }
|
||||
},
|
||||
showUnitcell: false,
|
||||
representationPreset: 'auto'
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { QueryContext, StructureSelection } from '../../mol-model/structure';
|
||||
import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
|
||||
import { superpose } from '../../mol-model/structure/structure/util/superposition';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
@@ -79,7 +79,7 @@ export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], c
|
||||
const xs = plugin.managers.structure.hierarchy.current.structures;
|
||||
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.cell.obj!.data))));
|
||||
|
||||
const transforms = superposeStructures(selections);
|
||||
const transforms = superpose(selections);
|
||||
|
||||
await siteVisual(plugin, xs[0].cell, pivot, rest);
|
||||
for (let i = 1; i < selections.length; i++) {
|
||||
|
||||
@@ -38,16 +38,16 @@
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='controls'></div>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
<script>
|
||||
LightingDemo.init('app')
|
||||
LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
|
||||
|
||||
|
||||
addHeader('Example PDB IDs');
|
||||
addControl('1M07', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' }));
|
||||
addControl('6HY0', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6HY0.cif', assemblyId: '1' }));
|
||||
|
||||
@@ -105,7 +105,7 @@ class LightingDemo {
|
||||
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
|
||||
const model = await this.plugin.builders.structure.createModel(trajectory);
|
||||
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } });
|
||||
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'model', params: { } });
|
||||
|
||||
const polymer = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer');
|
||||
if (polymer) await this.plugin.builders.structure.representation.addRepresentation(polymer, { type: 'spacefill', color: 'illustrative' });
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -65,7 +65,7 @@
|
||||
<div id="app"></div>
|
||||
<div id="volume-streaming-wrapper"></div>
|
||||
<script>
|
||||
// it might be a good idea to define these colors in a separate script file
|
||||
// it might be a good idea to define these colors in a separate script file
|
||||
var CustomColors = [0x00ff00, 0x0000ff];
|
||||
|
||||
// create an instance of the plugin
|
||||
@@ -74,11 +74,11 @@
|
||||
console.log('Wrapper version', MolStarProteopediaWrapper.VERSION_MAJOR, MolStarProteopediaWrapper.VERSION_MINOR);
|
||||
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
|
||||
var pdbId = '1cbs', assemblyId= 'preferred', isBinary = true;
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/entry-files/download/' + pdbId + '.bcif'
|
||||
var format = 'cif';
|
||||
|
||||
|
||||
$('url').value = url;
|
||||
$('url').onchange = function (e) { url = e.target.value; }
|
||||
$('assemblyId').value = assemblyId;
|
||||
@@ -88,9 +88,11 @@
|
||||
$('isBinary').checked = isBinary;
|
||||
$('isBinary').onchange = function (e) { isBinary = !!e.target.checked; };
|
||||
|
||||
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
|
||||
// var format = 'pdb';
|
||||
// var assemblyId = 'deposited';
|
||||
function loadAndSnapshot(params) {
|
||||
PluginWrapper.load(params).then(() => {
|
||||
setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);
|
||||
});
|
||||
}
|
||||
|
||||
var representationStyle = {
|
||||
// sequence: { coloring: 'proteopedia-custom' }, // or just { }
|
||||
@@ -103,7 +105,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 +113,8 @@
|
||||
listHetGroups(info);
|
||||
});
|
||||
|
||||
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format, isBinary }));
|
||||
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, isBinary, assemblyId: assemblyId }));
|
||||
addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
|
||||
addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
@@ -138,7 +140,7 @@
|
||||
// Same as "wheel icon" and Viewport options
|
||||
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
|
||||
|
||||
addSeparator();
|
||||
|
||||
addHeader('Animation');
|
||||
@@ -171,7 +173,7 @@
|
||||
addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
|
||||
addControl('Remove', () => PluginWrapper.experimentalData.remove());
|
||||
|
||||
addSeparator();
|
||||
addSeparator();
|
||||
addHeader('State');
|
||||
|
||||
var snapshot;
|
||||
@@ -185,10 +187,10 @@
|
||||
PluginWrapper.snapshot.set(snapshot);
|
||||
});
|
||||
addControl('Download State', () => {
|
||||
snapshot = PluginWrapper.snapshot.download('molj');
|
||||
PluginWrapper.snapshot.download('molj');
|
||||
});
|
||||
addControl('Download Session', () => {
|
||||
snapshot = PluginWrapper.snapshot.download('molx');
|
||||
PluginWrapper.snapshot.download('molx');
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
@@ -5,35 +5,34 @@
|
||||
*/
|
||||
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { Scheduler } from '../../mol-task';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { getFormattedTime } from '../../mol-util/date';
|
||||
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { getFormattedTime } from '../../mol-util/date';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
|
||||
import './index.html';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
require('../../mol-plugin-ui/skin/light.scss');
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
static VERSION_MAJOR = 5;
|
||||
static VERSION_MINOR = 4;
|
||||
static VERSION_MINOR = 5;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -90,9 +89,12 @@ class MolStarProteopediaWrapper {
|
||||
private structure(assemblyId: string) {
|
||||
const model = this.state.build().to(StateElements.Model);
|
||||
const props = {
|
||||
type: {
|
||||
type: assemblyId ? {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
params: { id: assemblyId }
|
||||
} : {
|
||||
name: 'model' as const,
|
||||
params: { }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -197,7 +199,7 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
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) {
|
||||
async load({ url, format = 'cif', assemblyId = '', isBinary = false, representationStyle }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
@@ -221,9 +223,12 @@ class MolStarProteopediaWrapper {
|
||||
const info = await this.doInfo(true);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
const props = {
|
||||
type: {
|
||||
type: assemblyId ? {
|
||||
name: 'assembly' as const,
|
||||
params: { id: asmId || 'deposited' }
|
||||
params: { id: asmId }
|
||||
} : {
|
||||
name: 'model' as const,
|
||||
params: { }
|
||||
}
|
||||
};
|
||||
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
|
||||
@@ -233,7 +238,6 @@ class MolStarProteopediaWrapper {
|
||||
await this.updateStyle(representationStyle);
|
||||
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
|
||||
}
|
||||
|
||||
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
|
||||
@@ -251,9 +255,7 @@ class MolStarProteopediaWrapper {
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
viewport = {
|
||||
@@ -407,7 +409,7 @@ class MolStarProteopediaWrapper {
|
||||
try {
|
||||
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
|
||||
this.loadedParams = { ...this.emptyLoadedParams };
|
||||
await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
|
||||
return await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ThemeDataContext } from '../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { getPalette } from '../../mol-util/color/palette';
|
||||
import { ColorTheme, LocationColor } from '../../mol-theme/color';
|
||||
import { ScaleLegend, TableLegend } from '../../mol-util/legend';
|
||||
import { StructureElement, Bond } from '../../mol-model/structure';
|
||||
import { Location } from '../../mol-model/location';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { distinctColors } from '../../mol-util/color/distinct';
|
||||
import { Hcl } from '../../mol-util/color/spaces/hcl';
|
||||
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Gives every model in a CellPack packing a unique color similar to other models in the packing.';
|
||||
|
||||
export const CellPackColorThemeParams = {};
|
||||
export type CellPackColorThemeParams = typeof CellPackColorThemeParams
|
||||
export function getCellPackColorThemeParams(ctx: ThemeDataContext) {
|
||||
return CellPackColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackColorThemeParams>): ColorTheme<CellPackColorThemeParams> {
|
||||
let color: LocationColor;
|
||||
let legend: ScaleLegend | TableLegend | undefined;
|
||||
|
||||
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
|
||||
|
||||
if (ctx.structure && info) {
|
||||
const colors = distinctColors(info.packingsCount);
|
||||
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
|
||||
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
|
||||
|
||||
const { models } = ctx.structure.root;
|
||||
|
||||
let size = 0;
|
||||
for (const m of models) size = Math.max(size, m.trajectoryInfo.size);
|
||||
|
||||
const palette = getPalette(size, { palette: {
|
||||
name: 'generate',
|
||||
params: {
|
||||
hue, chroma: [30, 80], luminance: [15, 85],
|
||||
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75,
|
||||
minLabel: 'Min', maxLabel: 'Max', valueLabel: (i: number) => `${i + 1}`,
|
||||
}
|
||||
}});
|
||||
legend = palette.legend;
|
||||
const modelColor = new Map<number, Color>();
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
const idx = models[i].trajectoryInfo.index;
|
||||
modelColor.set(models[i].trajectoryInfo.index, palette.color(idx));
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
if (StructureElement.Location.is(location)) {
|
||||
return modelColor.get(location.unit.model.trajectoryInfo.index)!;
|
||||
} else if (Bond.isLocation(location)) {
|
||||
return modelColor.get(location.aUnit.model.trajectoryInfo.index)!;
|
||||
}
|
||||
return DefaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: CellPackColorTheme,
|
||||
granularity: 'instance',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend
|
||||
};
|
||||
}
|
||||
|
||||
export const CellPackColorThemeProvider: ColorTheme.Provider<CellPackColorThemeParams, 'cellpack'> = {
|
||||
name: 'cellpack',
|
||||
label: 'CellPack',
|
||||
category: ColorTheme.Category.Chain,
|
||||
factory: CellPackColorTheme,
|
||||
getParams: getCellPackColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(CellPackColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => {
|
||||
return (
|
||||
!!ctx.structure && ctx.structure.elementCount > 0 &&
|
||||
ctx.structure.models[0].trajectoryInfo.size > 1 &&
|
||||
!!CellPackInfoProvider.get(ctx.structure).value
|
||||
);
|
||||
}
|
||||
};
|
||||
97
src/extensions/cellpack/color/generate.ts
Normal file
97
src/extensions/cellpack/color/generate.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { getPalette } from '../../../mol-util/color/palette';
|
||||
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
|
||||
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
|
||||
import { StructureElement, Bond, Model } from '../../../mol-model/structure';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CellPackInfoProvider } from '../property';
|
||||
import { distinctColors } from '../../../mol-util/color/distinct';
|
||||
import { Hcl } from '../../../mol-util/color/spaces/hcl';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Gives every model in a CellPack packing a unique generated color similar to other models in the packing.';
|
||||
|
||||
export const CellPackGenerateColorThemeParams = {};
|
||||
export type CellPackGenerateColorThemeParams = typeof CellPackGenerateColorThemeParams
|
||||
export function getCellPackGenerateColorThemeParams(ctx: ThemeDataContext) {
|
||||
return CellPackGenerateColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackGenerateColorThemeParams>): ColorTheme<CellPackGenerateColorThemeParams> {
|
||||
let color: LocationColor;
|
||||
let legend: ScaleLegend | TableLegend | undefined;
|
||||
|
||||
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
|
||||
|
||||
if (ctx.structure && info) {
|
||||
const colors = distinctColors(info.packingsCount);
|
||||
let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
|
||||
|
||||
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
|
||||
|
||||
const { models } = ctx.structure.root;
|
||||
|
||||
let size = 0;
|
||||
for (const m of models) size = Math.max(size, Model.TrajectoryInfo.get(m).size);
|
||||
|
||||
const palette = getPalette(size, { palette: {
|
||||
name: 'generate',
|
||||
params: {
|
||||
hue, chroma: [30, 80], luminance: [15, 85],
|
||||
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75,
|
||||
minLabel: 'Min', maxLabel: 'Max', valueLabel: (i: number) => `${i + 1}`,
|
||||
}
|
||||
}});
|
||||
legend = palette.legend;
|
||||
const modelColor = new Map<number, Color>();
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
const idx = Model.TrajectoryInfo.get(models[i]).index;
|
||||
modelColor.set(Model.TrajectoryInfo.get(models[i]).index, palette.color(idx));
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
if (StructureElement.Location.is(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.unit.model).index)!;
|
||||
} else if (Bond.isLocation(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.aUnit.model).index)!;
|
||||
}
|
||||
return DefaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: CellPackGenerateColorTheme,
|
||||
granularity: 'instance',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend
|
||||
};
|
||||
}
|
||||
|
||||
export const CellPackGenerateColorThemeProvider: ColorTheme.Provider<CellPackGenerateColorThemeParams, 'cellpack-generate'> = {
|
||||
name: 'cellpack-generate',
|
||||
label: 'CellPack Generate',
|
||||
category: ColorTheme.Category.Chain,
|
||||
factory: CellPackGenerateColorTheme,
|
||||
getParams: getCellPackGenerateColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(CellPackGenerateColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => {
|
||||
return (
|
||||
!!ctx.structure && ctx.structure.elementCount > 0 &&
|
||||
Model.TrajectoryInfo.get(ctx.structure.models[0]).size > 1 &&
|
||||
!!CellPackInfoProvider.get(ctx.structure).value
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
72
src/extensions/cellpack/color/provided.ts
Normal file
72
src/extensions/cellpack/color/provided.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
|
||||
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
|
||||
import { StructureElement, Model } from '../../../mol-model/structure';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CellPackInfoProvider } from '../property';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Gives every model in a CellPack the color provied in the packing data.';
|
||||
|
||||
export const CellPackProvidedColorThemeParams = {};
|
||||
export type CellPackProvidedColorThemeParams = typeof CellPackProvidedColorThemeParams
|
||||
export function getCellPackProvidedColorThemeParams(ctx: ThemeDataContext) {
|
||||
return CellPackProvidedColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function CellPackProvidedColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackProvidedColorThemeParams>): ColorTheme<CellPackProvidedColorThemeParams> {
|
||||
let color: LocationColor;
|
||||
let legend: ScaleLegend | TableLegend | undefined;
|
||||
|
||||
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
|
||||
|
||||
if (ctx.structure && info?.colors) {
|
||||
const { models } = ctx.structure.root;
|
||||
const modelColor = new Map<number, Color>();
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
const idx = Model.TrajectoryInfo.get(models[i]).index;
|
||||
modelColor.set(Model.TrajectoryInfo.get(models[i]).index, info.colors[idx]);
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
return StructureElement.Location.is(location)
|
||||
? modelColor.get(Model.TrajectoryInfo.get(location.unit.model).index)!
|
||||
: DefaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: CellPackProvidedColorTheme,
|
||||
granularity: 'instance',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend
|
||||
};
|
||||
}
|
||||
|
||||
export const CellPackProvidedColorThemeProvider: ColorTheme.Provider<CellPackProvidedColorThemeParams, 'cellpack-provided'> = {
|
||||
name: 'cellpack-provided',
|
||||
label: 'CellPack Provided',
|
||||
category: ColorTheme.Category.Chain,
|
||||
factory: CellPackProvidedColorTheme,
|
||||
getParams: getCellPackProvidedColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(CellPackProvidedColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => {
|
||||
return (
|
||||
!!ctx.structure && ctx.structure.elementCount > 0 &&
|
||||
Model.TrajectoryInfo.get(ctx.structure.models[0]).size > 1 &&
|
||||
!!CellPackInfoProvider.get(ctx.structure).value?.colors
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -191,8 +191,14 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
|
||||
|
||||
const rpTmpVec1 = Vec3();
|
||||
|
||||
export function getMatFromResamplePoints(points: NumberArray, segmentLength: number) {
|
||||
const new_points = ResampleControlPoints(points, segmentLength);
|
||||
export function getMatFromResamplePoints(points: NumberArray, segmentLength: number, resample: boolean) {
|
||||
let new_points: Vec3[] = [];
|
||||
if (resample) new_points = ResampleControlPoints(points, segmentLength);
|
||||
else {
|
||||
for (let idx = 0; idx < points.length / 3; ++idx){
|
||||
new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
|
||||
}
|
||||
}
|
||||
const npoints = new_points.length;
|
||||
const new_normal = GetSmoothNormals(new_points);
|
||||
const frames = GetMiniFrame(new_points, new_normal);
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface Ingredient {
|
||||
radii?: [Radii];
|
||||
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
|
||||
nbCurve?: number;
|
||||
uLength?: number;
|
||||
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
|
||||
[curveX: string]: unknown;
|
||||
/** the orientation in the membrane */
|
||||
@@ -66,6 +67,8 @@ export interface Ingredient {
|
||||
/** offset along membrane */
|
||||
offset?: Vec3;
|
||||
ingtype?: string;
|
||||
color?: Vec3;
|
||||
confidence?: number;
|
||||
}
|
||||
|
||||
export interface IngredientSource {
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior';
|
||||
import { CellPackColorThemeProvider } from './color';
|
||||
import { LoadCellPackModel } from './model';
|
||||
|
||||
import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
import { CellPackProvidedColorThemeProvider } from './color/provided';
|
||||
|
||||
export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
|
||||
name: 'cellpack',
|
||||
@@ -19,12 +19,14 @@ export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
|
||||
register(): void {
|
||||
this.ctx.state.data.actions.add(LoadCellPackModel);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackGenerateColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackProvidedColorThemeProvider);
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.state.data.actions.remove(LoadCellPackModel);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackGenerateColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackProvidedColorThemeProvider);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -17,7 +17,7 @@ import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
|
||||
import { SymmetryOperator } from '../../mol-math/geometry';
|
||||
import { Task, RuntimeContext } from '../../mol-task';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies } from './state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { getMatFromResamplePoints } from './curve';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
@@ -27,61 +27,76 @@ import { Column } from '../../mol-data/db';
|
||||
import { createModels } from '../../mol-model-formats/structure/basic/parser';
|
||||
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`;
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, file?: Asset.File) {
|
||||
class TrajectoryCache {
|
||||
private map = new Map<string, Model.Trajectory>();
|
||||
set(id: string, trajectory: Model.Trajectory) { this.map.set(id, trajectory); }
|
||||
get(id: string) { return this.map.get(id); }
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
|
||||
const assetManager = plugin.managers.asset;
|
||||
const model_id = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
|
||||
let model: Model;
|
||||
let trajectory = trajCache.get(id);
|
||||
let assets: Asset.Wrapper[] = [];
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const cif = (await parseCif(plugin, text.data)).blocks[0];
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
|
||||
assets.push(binary);
|
||||
const cif = (await parseCif(plugin, binary.data)).blocks[0];
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
|
||||
} else if (file.name.endsWith('.pdb')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const pdb = await parsePDBfile(plugin, text.data, id);
|
||||
model = (await plugin.runTask(trajectoryFromPDB(pdb)))[model_id];
|
||||
} else {
|
||||
throw new Error(`unsupported file type '${file.name}'`);
|
||||
}
|
||||
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
if (surface){
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
if (data.asset){
|
||||
assets.push(data.asset);
|
||||
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
|
||||
if (!trajectory) {
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const cif = (await parseCif(plugin, text.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
|
||||
assets.push(binary);
|
||||
const cif = (await parseCif(plugin, binary.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.pdb')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const pdb = await parsePDBfile(plugin, text.data, id);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(pdb));
|
||||
} else {
|
||||
throw new Error(`unsupported file type '${file.name}'`);
|
||||
}
|
||||
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
if (surface){
|
||||
try {
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
assets.push(data.asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} catch (e) {
|
||||
// fallback to getFromPdb
|
||||
// console.error(e);
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
|
||||
}
|
||||
} else {
|
||||
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
|
||||
assets.push(data.asset);
|
||||
if ('pdb' in data) {
|
||||
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
|
||||
} else {
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(data.mmcif)))[model_id];
|
||||
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
|
||||
assets.push(data.asset);
|
||||
if ('pdb' in data) {
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} else {
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(data.mmcif));
|
||||
}
|
||||
}
|
||||
trajCache.set(id, trajectory);
|
||||
}
|
||||
const model = trajectory[modelIndex];
|
||||
return { model, assets };
|
||||
}
|
||||
|
||||
@@ -94,7 +109,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
|
||||
}
|
||||
let query;
|
||||
if (source.selection){
|
||||
const asymIds: string[] = source.selection.replace(' :', '').split(' or');
|
||||
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
|
||||
query = MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
|
||||
@@ -133,7 +148,6 @@ function getTransform(trans: Vec3, rot: Quat) {
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
|
||||
if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]));
|
||||
else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]));
|
||||
@@ -142,11 +156,15 @@ function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
|
||||
function getCurveTransforms(ingredient: Ingredient) {
|
||||
const n = ingredient.nbCurve || 0;
|
||||
const instances: Mat4[] = [];
|
||||
const segmentLength = ingredient.radii
|
||||
? (ingredient.radii[0].radii
|
||||
let segmentLength = 3.4;
|
||||
if (ingredient.uLength){
|
||||
segmentLength = ingredient.uLength;
|
||||
} else if (ingredient.radii){
|
||||
segmentLength = ingredient.radii[0].radii
|
||||
? ingredient.radii[0].radii[0] * 2.0
|
||||
: 3.4)
|
||||
: 3.4;
|
||||
: 3.4;
|
||||
}
|
||||
let resampling: boolean = false;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const cname = `curve${i}`;
|
||||
if (!(cname in ingredient)) {
|
||||
@@ -158,12 +176,17 @@ function getCurveTransforms(ingredient: Ingredient) {
|
||||
// TODO handle curve with 2 or less points
|
||||
continue;
|
||||
}
|
||||
// test for resampling
|
||||
let distance: number = Vec3.distance(_points[0], _points[1]);
|
||||
if (distance >= segmentLength + 2.0) {
|
||||
console.info(distance);
|
||||
resampling = true;
|
||||
}
|
||||
const points = new Float32Array(_points.length * 3);
|
||||
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3);
|
||||
const newInstances = getMatFromResamplePoints(points, segmentLength);
|
||||
const newInstances = getMatFromResamplePoints(points, segmentLength, resampling);
|
||||
instances.push(...newInstances);
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
@@ -277,7 +300,6 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
|
||||
async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model);
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = MmcifFormat.fromFrame(cif);
|
||||
const models = await createModels(format.data.db, format, ctx);
|
||||
@@ -288,22 +310,22 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
|
||||
return getStructure(plugin, curveModel, ingredient.source);
|
||||
}
|
||||
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
|
||||
const { name, source, results, nbCurve } = ingredient;
|
||||
if (source.pdb === 'None') return;
|
||||
|
||||
const file = ingredientFiles[source.pdb];
|
||||
if (!file) {
|
||||
// TODO can these be added to the library?
|
||||
if (name === 'HIV1_CAhex_0_1_0') return;
|
||||
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return;
|
||||
if (name === 'iLDL') return;
|
||||
if (name === 'peptides') return;
|
||||
if (name === 'HIV1_CAhex_0_1_0') return; // 1VU4CtoH_hex.pdb
|
||||
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return; // 1AK4fitTo1VU4hex.pdb
|
||||
if (name === 'iLDL') return; // EMD-5239
|
||||
if (name === 'peptides') return; // peptide.pdb
|
||||
if (name === 'lypoglycane') return;
|
||||
}
|
||||
|
||||
// model id in case structure is NMR
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, file);
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
|
||||
if (!model) return;
|
||||
|
||||
let structure: Structure;
|
||||
@@ -354,13 +376,22 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
return Task.create('Create Packing Structure', async ctx => {
|
||||
const { ingredients, name } = packing;
|
||||
const assets: Asset.Wrapper[] = [];
|
||||
const trajCache = new TrajectoryCache();
|
||||
const structures: Structure[] = [];
|
||||
const colors: Color[] = [];
|
||||
let skipColors: boolean = false;
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) await ctx.update(iName);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
|
||||
if (ingredientStructure) {
|
||||
structures.push(ingredientStructure.structure);
|
||||
assets.push(...ingredientStructure.assets);
|
||||
const c = ingredients[iName].color;
|
||||
if (c){
|
||||
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
|
||||
} else {
|
||||
skipColors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,11 +412,9 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
|
||||
const structure = builder.getStructure();
|
||||
for( let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
const { trajectoryInfo } = structure.models[i];
|
||||
trajectoryInfo.size = il;
|
||||
trajectoryInfo.index = i;
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
return { structure, assets };
|
||||
return { structure, assets, colors: skipColors ? undefined : colors };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -421,11 +450,25 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!file){
|
||||
// check for cif directly
|
||||
const cifileName = `${name}.cif`;
|
||||
for (const f of params.ingredients.files) {
|
||||
if (cifileName === f.name) {
|
||||
file = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let b = state.build().toRoot();
|
||||
if (file) {
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
|
||||
if (file.name.endsWith('.cif')) {
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
|
||||
}
|
||||
} else {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
|
||||
@@ -434,16 +477,19 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.StructureFromModel)
|
||||
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
|
||||
const membraneParams = {
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
}
|
||||
|
||||
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
||||
const ingredientFiles = params.ingredients.files || [];
|
||||
|
||||
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
|
||||
if (params.source.name === 'id') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
|
||||
@@ -451,12 +497,25 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
|
||||
} else {
|
||||
const file = params.source.params;
|
||||
if (file === null) {
|
||||
if (!file?.file) {
|
||||
plugin.log.error('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonFile: Asset.File;
|
||||
if (file.name.toLowerCase().endsWith('.zip')) {
|
||||
const data = await readFromFile(file.file, 'zip').runInContext(runtime);
|
||||
jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
|
||||
objectForEach(data, (v, k) => {
|
||||
if (k === 'model.json') return;
|
||||
ingredientFiles.push(Asset.File(new File([v], k)));
|
||||
});
|
||||
} else {
|
||||
jsonFile = file;
|
||||
}
|
||||
|
||||
cellPackJson = state.build().toRoot()
|
||||
.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
|
||||
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
|
||||
}
|
||||
|
||||
const cellPackBuilder = cellPackJson
|
||||
@@ -469,7 +528,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
await handleHivRna(plugin, packings, params.baseUrl);
|
||||
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files };
|
||||
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles };
|
||||
|
||||
const packing = await state.build()
|
||||
.to(cellPackBuilder.ref)
|
||||
@@ -481,7 +540,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackPackingPreset.apply(packing, packingParams, plugin);
|
||||
if ( packings[i].location === 'surface' ){
|
||||
if ( packings[i].location === 'surface' && params.membrane){
|
||||
await loadMembrane(plugin, packings[i].name, state, params);
|
||||
}
|
||||
}
|
||||
@@ -489,18 +548,21 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
|
||||
const LoadCellPackModelParams = {
|
||||
source: PD.MappedStatic('id', {
|
||||
'id': PD.Select('influenza_model1.json', [
|
||||
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
|
||||
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
||||
['hiv_lipids.bcif', 'hiv_lipids'],
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['ExosomeModel.json', 'ExosomeModel'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
] as const),
|
||||
'file': PD.File({ accept: 'id' }),
|
||||
'id': PD.Select('InfluenzaModel2.json', [
|
||||
['blood_hiv_immature_inside.json', 'Blood HIV immature'],
|
||||
['HIV_immature_model.json', 'HIV immature'],
|
||||
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'Blood HIV'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV'],
|
||||
['influenza_model1.json', 'Influenza envelope'],
|
||||
['InfluenzaModel2.json', 'Influenza Complete'],
|
||||
['ExosomeModel.json', 'Exosome Model'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
|
||||
['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
|
||||
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
|
||||
}, { options: [['id', 'Id'], ['file', 'File']] }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
membrane: PD.Boolean(true),
|
||||
ingredients : PD.Group({
|
||||
files: PD.FileList({ accept: '.cif,.bcif,.pdb' })
|
||||
}, { isExpanded: true }),
|
||||
@@ -516,9 +578,5 @@ export const LoadCellPackModel = StateAction.build({
|
||||
params: LoadCellPackModelParams,
|
||||
from: PSO.Root
|
||||
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
|
||||
if (params.source.name === 'id' && params.source.params === 'hiv_lipids.bcif') {
|
||||
await loadMembrane(ctx, 'hiv_lipids', state, params);
|
||||
} else {
|
||||
await loadPackings(ctx, taskCtx, state, params);
|
||||
}
|
||||
await loadPackings(ctx, taskCtx, state, params);
|
||||
}));
|
||||
@@ -8,7 +8,9 @@ import { StateObjectRef } from '../../mol-state';
|
||||
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { CellPackColorThemeProvider } from './color';
|
||||
import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { CellPackProvidedColorThemeProvider } from './color/provided';
|
||||
|
||||
export const CellpackPackingPresetParams = {
|
||||
traceOnly: PD.Boolean(true),
|
||||
@@ -40,8 +42,10 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
Object.assign(reprProps, { sizeFactor: 2 });
|
||||
}
|
||||
|
||||
const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
|
||||
const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
|
||||
const color = CellPackColorThemeProvider.name;
|
||||
const representations = {
|
||||
polymer: builder.buildRepresentation<any>(update, components.polymer, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color }, { tag: 'polymer' })
|
||||
};
|
||||
@@ -85,6 +89,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
@@ -5,17 +5,20 @@
|
||||
*/
|
||||
|
||||
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
|
||||
import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
|
||||
export type CellPackInfoValue = {
|
||||
packingsCount: number
|
||||
packingIndex: number
|
||||
colors?: Color[]
|
||||
}
|
||||
|
||||
const CellPackInfoParams = {
|
||||
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0 }, { isHidden: true })
|
||||
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0, colors: undefined }, { isHidden: true })
|
||||
};
|
||||
type CellPackInfoParams = PD.Values<typeof CellPackInfoParams>
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import { IngredientFiles } from './util';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { Structure, StructureSymmetry, Unit, Model } from '../../mol-model/structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
|
||||
|
||||
@@ -71,10 +73,9 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
|
||||
ingredientFiles[file.name] = file;
|
||||
}
|
||||
}
|
||||
const { structure, assets } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
|
||||
|
||||
const { structure, assets, colors } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
|
||||
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
|
||||
info: { packingsCount: a.data.packings.length, packingIndex: params.packing }
|
||||
info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
|
||||
});
|
||||
|
||||
(cache as any).assets = assets;
|
||||
@@ -94,4 +95,56 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export { StructureFromAssemblies };
|
||||
type StructureFromAssemblies = typeof StructureFromAssemblies
|
||||
const StructureFromAssemblies = PluginStateTransform.BuiltIn({
|
||||
name: 'Structure from all assemblies',
|
||||
display: { name: 'Structure from all assemblies' },
|
||||
from: PSO.Molecule.Model,
|
||||
to: PSO.Molecule.Structure,
|
||||
params: {
|
||||
}
|
||||
})({
|
||||
canAutoUpdate({ newParams }) {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
return Task.create('Build Structure', async ctx => {
|
||||
// TODO: optimze
|
||||
// TODO: think of ways how to fast-track changes to this for animations
|
||||
const model = a.data;
|
||||
let initial_structure = Structure.ofModel(model);
|
||||
const structures: Structure[] = [];
|
||||
let structure: Structure = initial_structure;
|
||||
// the list of asambly *?
|
||||
const symmetry = ModelSymmetry.Provider.get(model);
|
||||
if (symmetry && symmetry.assemblies.length !== 0){
|
||||
for (const a of symmetry.assemblies) {
|
||||
const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
|
||||
structures.push(s);
|
||||
}
|
||||
const builder = Structure.Builder({ label: name });
|
||||
let offsetInvariantId = 0;
|
||||
for (const s of structures) {
|
||||
let maxInvariantId = 0;
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId;
|
||||
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
|
||||
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
|
||||
}
|
||||
offsetInvariantId += maxInvariantId + 1;
|
||||
}
|
||||
structure = builder.getStructure();
|
||||
for( let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
}
|
||||
return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
|
||||
});
|
||||
},
|
||||
dispose({ b }) {
|
||||
b?.data.customPropertyDescriptors.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
import { Column, Table } from '../../mol-data/db';
|
||||
import { toTable } from '../../mol-io/reader/cif/schema';
|
||||
import { CifWriter } from '../../mol-io/writer/cif';
|
||||
import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
|
||||
import { Model } from '../../mol-model/structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
|
||||
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
|
||||
export namespace PDBePreferredAssembly {
|
||||
export type Property = string
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
import { Column, Table } from '../../mol-data/db';
|
||||
import { toTable } from '../../mol-io/reader/cif/schema';
|
||||
import { CifWriter } from '../../mol-io/writer/cif';
|
||||
import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
|
||||
import { Model } from '../../mol-model/structure';
|
||||
import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
|
||||
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
|
||||
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
|
||||
export namespace PDBeStructRefDomain {
|
||||
export type Property = PropertyWrapper<Table<Schema['pdbe_struct_ref_domain']> | undefined>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Column, Table } from '../../../mol-data/db';
|
||||
import { toTable } from '../../../mol-io/reader/cif/schema';
|
||||
import { mmCIF_residueId_schema } from '../../../mol-io/reader/cif/schema/mmcif-extras';
|
||||
import { CifWriter } from '../../../mol-io/writer/cif';
|
||||
import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
|
||||
import { Model, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
|
||||
import { residueIdFields } from '../../../mol-model/structure/export/categories/atom_site';
|
||||
import { StructureElement, CifExportContext, Structure } from '../../../mol-model/structure/structure';
|
||||
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
|
||||
@@ -22,6 +22,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
|
||||
import { Asset } from '../../../mol-util/assets';
|
||||
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
|
||||
|
||||
export { StructureQualityReport };
|
||||
|
||||
|
||||
@@ -181,8 +181,8 @@ export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
|
||||
}
|
||||
|
||||
const assemblySymmetry = await tryCreateAssemblySymmetry(plugin, structureCell);
|
||||
const globalThemeName = assemblySymmetry.isOk ? Tag.Cluster as any : undefined;
|
||||
const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName }, plugin);
|
||||
const colorTheme = assemblySymmetry.isOk ? Tag.Cluster as any : undefined;
|
||||
const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme, focusThemeName: colorTheme }, plugin);
|
||||
|
||||
return { components: preset.components, representations: { ...preset.representations, assemblySymmetry } };
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from '../graphq
|
||||
import query from '../graphql/symmetry.gql';
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { CustomPropertyDescriptor, Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
|
||||
import { Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
|
||||
import { Database as _Database, Column } from '../../../mol-data/db';
|
||||
import { GraphQLClient } from '../../../mol-util/graphql-client';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
@@ -19,6 +19,7 @@ import { ReadonlyVec3 } from '../../../mol-math/linear-algebra/3d/vec3';
|
||||
import { SetUtils } from '../../../mol-util/set';
|
||||
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
import { compile } from '../../../mol-script/runtime/query/compiler';
|
||||
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
|
||||
|
||||
const BiologicalAssemblyNames = new Set([
|
||||
'author_and_software_defined_assembly',
|
||||
@@ -34,7 +35,7 @@ export function isBiologicalAssembly(structure: Structure): boolean {
|
||||
const mmcif = structure.models[0].sourceData.data.db;
|
||||
if (!mmcif.pdbx_struct_assembly.details.isDefined) return false;
|
||||
const id = structure.units[0].conformation.operator.assembly?.id || '';
|
||||
if (id === '' || id === 'deposited') return true;
|
||||
if (id === '') return true;
|
||||
const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id);
|
||||
if (indices.length !== 1) return false;
|
||||
const details = mmcif.pdbx_struct_assembly.details.value(indices[0]);
|
||||
@@ -62,7 +63,7 @@ export namespace AssemblySymmetry {
|
||||
|
||||
const client = new GraphQLClient(props.serverUrl, ctx.assetManager);
|
||||
const variables: AssemblySymmetryQueryVariables = {
|
||||
assembly_id: structure.units[0].conformation.operator.assembly?.id || 'deposited',
|
||||
assembly_id: structure.units[0].conformation.operator.assembly?.id || '',
|
||||
entry_id: structure.units[0].model.entryId
|
||||
};
|
||||
const result = await client.request(ctx.runtime, query, variables);
|
||||
|
||||
@@ -186,12 +186,22 @@ const getSymbolCage = memoize1((symbol: string): Cage | undefined => {
|
||||
if (symbol.startsWith('D') || symbol.startsWith('C')) {
|
||||
// z axis is prism axis, x/y axes cut through edge midpoints
|
||||
const fold = parseInt(symbol.substr(1));
|
||||
let cage: Cage;
|
||||
if (fold === 2) {
|
||||
return PrismCage(polygon(4, false));
|
||||
cage = PrismCage(polygon(4, false));
|
||||
} else if (fold === 3) {
|
||||
return WedgeCage();
|
||||
cage = WedgeCage();
|
||||
} else if (fold > 3) {
|
||||
return PrismCage(polygon(fold, false));
|
||||
cage = PrismCage(polygon(fold, false));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (fold % 2 === 0) {
|
||||
return cage;
|
||||
} else {
|
||||
const m = Mat4.identity();
|
||||
Mat4.rotate(m, m, 1 / fold * Math.PI / 2, Vec3.unitZ);
|
||||
return transformCage(cloneCage(cage), m);
|
||||
}
|
||||
} else if (symbol === 'O') {
|
||||
// x/y/z axes cut through order 4 vertices
|
||||
@@ -225,6 +235,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;
|
||||
|
||||
@@ -236,7 +247,7 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
|
||||
pair = axes.filter(a => a.order === 2);
|
||||
} else if (fold >= 3) {
|
||||
const aN = axes.filter(a => a.order === fold)[0];
|
||||
const a2 = axes.filter(a => a.order === 2)[0];
|
||||
const a2 = axes.filter(a => a.order === 2)[1];
|
||||
pair = [aN, a2];
|
||||
}
|
||||
} else if (symbol === 'O') {
|
||||
@@ -246,8 +257,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,10 +274,22 @@ 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));
|
||||
|
||||
if (symbol.startsWith('D')) {
|
||||
const { sphere } = structure.lookup3d.boundary;
|
||||
let sizeXY = (sphere.radius * 2) * 0.8; // fallback for missing extrema
|
||||
if (Sphere3D.hasExtrema(sphere)) {
|
||||
const n = Mat3.directionTransform(Mat3(), t);
|
||||
const dirs = unitCircleDirections.map(d => Vec3.transformMat3(Vec3(), d, n));
|
||||
sizeXY = getMaxProjectedDistance(sphere.extrema, dirs, sphere.center) * 1.6;
|
||||
}
|
||||
Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, Vec3.distance(aA.start, aA.end) * 0.9));
|
||||
} else {
|
||||
Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol));
|
||||
}
|
||||
} else {
|
||||
if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, aA.end, aA.start)) === 0) {
|
||||
Vec3.copy(up, Vec3.unitY);
|
||||
@@ -282,7 +305,6 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
|
||||
const dirs = unitCircleDirections.map(d => Vec3.transformMat3(Vec3(), d, n));
|
||||
sizeXY = getMaxProjectedDistance(sphere.extrema, dirs, sphere.center);
|
||||
}
|
||||
|
||||
Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, size * 0.9));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ import { StateAction, StateSelection } from '../../../mol-state';
|
||||
import { PluginStateObject } from '../../../mol-plugin-state/objects';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { Task } from '../../../mol-task';
|
||||
import Check from '@material-ui/icons/Check';
|
||||
import Extension from '@material-ui/icons/Extension';
|
||||
import { ExtensionSvg, CheckSvg } from '../../../mol-plugin-ui/controls/icons';
|
||||
|
||||
interface AssemblySymmetryControlState extends CollapsableState {
|
||||
isBusy: boolean
|
||||
@@ -30,7 +29,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
|
||||
isCollapsed: false,
|
||||
isBusy: false,
|
||||
isHidden: true,
|
||||
brand: { accent: 'cyan', svg: Extension }
|
||||
brand: { accent: 'cyan', svg: ExtensionSvg }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,7 +61,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
|
||||
renderEnable() {
|
||||
const pivot = this.pivot;
|
||||
if (!pivot.cell.parent) return null;
|
||||
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: Check }} />;
|
||||
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: CheckSvg }} />;
|
||||
}
|
||||
|
||||
renderNoSymmetries() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -223,12 +222,12 @@ function densityFitLabel(loci: Loci): string | undefined {
|
||||
if (rsrzSeen.size) {
|
||||
const rsrzCount = `<small>(${rsrzSeen.size} ${rsrzSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
|
||||
const rsrzAvg = rsrzSum / rsrzSeen.size;
|
||||
summary.push(`Real Space R ${rsrzCount}: ${rsrzAvg.toFixed(2)}`);
|
||||
summary.push(`Real-Space R Z-score ${rsrzCount}: ${rsrzAvg.toFixed(2)}`);
|
||||
}
|
||||
if (rsccSeen.size) {
|
||||
const rsccCount = `<small>(${rsccSeen.size} ${rsccSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
|
||||
const rsccAvg = rsccSum / rsccSeen.size;
|
||||
summary.push(`Real Space Correlation Coefficient ${rsccCount}: ${rsccAvg.toFixed(2)}`);
|
||||
summary.push(`Real-Space Correlation Coefficient ${rsccCount}: ${rsccAvg.toFixed(2)}`);
|
||||
}
|
||||
|
||||
if (summary.length) {
|
||||
@@ -310,27 +309,28 @@ export const ValidationReportGeometryQualityPreset = StructureRepresentationPres
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const model = structureCell?.obj?.data.model;
|
||||
if (!structureCell || !model) return {};
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
await plugin.runTask(Task.create('Validation Report', async runtime => {
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, structure.models[0]);
|
||||
}));
|
||||
|
||||
const colorTheme = GeometryQualityColorThemeProvider.name as any;
|
||||
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
|
||||
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme, focusThemeName: colorTheme }, plugin);
|
||||
|
||||
const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' });
|
||||
|
||||
const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
let clashesBallAndStick, clashesSnfg3d;
|
||||
let clashesBallAndStick, clashesRepr;
|
||||
if (representations) {
|
||||
clashesBallAndStick = builder.buildRepresentation(update, clashes, { type: 'ball-and-stick', typeParams, color: colorTheme }, { tag: 'clashes-ball-and-stick' });
|
||||
clashesSnfg3d = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-snfg-3d' });
|
||||
clashesRepr = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-repr' });
|
||||
}
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } };
|
||||
|
||||
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesRepr } };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -341,20 +341,20 @@ export const ValidationReportDensityFitPreset = StructureRepresentationPresetPro
|
||||
description: 'Color structure based on density fit. Data from wwPDB Validation Report, obtained via RCSB PDB.'
|
||||
},
|
||||
isApplicable(a) {
|
||||
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0]);
|
||||
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromXray(a.data.models[0]) && Model.probablyHasDensityMap(a.data.models[0]);
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const model = structureCell?.obj?.data.model;
|
||||
if (!structureCell || !model) return {};
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
await plugin.runTask(Task.create('Validation Report', async runtime => {
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, structure.models[0]);
|
||||
}));
|
||||
|
||||
const colorTheme = DensityFitColorThemeProvider.name as any;
|
||||
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
|
||||
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme, focusThemeName: colorTheme }, plugin);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -370,14 +370,14 @@ export const ValidationReportRandomCoilIndexPreset = StructureRepresentationPres
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const model = structureCell?.obj?.data.model;
|
||||
if (!structureCell || !model) return {};
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
await plugin.runTask(Task.create('Validation Report', async runtime => {
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
|
||||
await ValidationReportProvider.attach({ runtime, assetManager: plugin.managers.asset }, structure.models[0]);
|
||||
}));
|
||||
|
||||
const colorTheme = RandomCoilIndexColorThemeProvider.name as any;
|
||||
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin);
|
||||
return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme, focusThemeName: colorTheme }, plugin);
|
||||
}
|
||||
});
|
||||
@@ -67,7 +67,7 @@ export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationRep
|
||||
factory: DensityFitColorTheme,
|
||||
getParams: () => ({}),
|
||||
defaultValues: PD.getDefaultValues({}),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.hasXrayMap(ctx.structure.models[0]),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.isFromXray(ctx.structure.models[0]) && Model.probablyHasDensityMap(ctx.structure.models[0]),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
|
||||
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { CustomPropertyDescriptor, Structure, Unit } from '../../../mol-model/structure';
|
||||
import { Structure, Unit } from '../../../mol-model/structure';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
|
||||
import { Model, ElementIndex, ResidueIndex } from '../../../mol-model/structure/model';
|
||||
@@ -21,6 +21,7 @@ import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
|
||||
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
|
||||
import Type from '../../../mol-script/language/type';
|
||||
import { Asset } from '../../../mol-util/assets';
|
||||
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
|
||||
|
||||
export { ValidationReport };
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Sphere3D } from '../mol-math/geometry';
|
||||
import { isDebugMode } from '../mol-util/debug';
|
||||
import { CameraHelperParams } from './helper/camera-helper';
|
||||
import { produce } from 'immer';
|
||||
import { HandleHelper, HandleHelperParams } from './helper/handle-helper';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
camera: PD.Group({
|
||||
@@ -60,7 +61,8 @@ export const Canvas3DParams = {
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
renderer: PD.Group(RendererParams),
|
||||
trackball: PD.Group(TrackballControlsParams),
|
||||
debug: PD.Group(DebugHelperParams)
|
||||
debug: PD.Group(DebugHelperParams),
|
||||
handle: PD.Group(HandleHelperParams),
|
||||
};
|
||||
export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
|
||||
export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
|
||||
@@ -132,12 +134,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);
|
||||
@@ -188,12 +190,13 @@ namespace Canvas3D {
|
||||
const controls = TrackballControls.create(input, camera, p.trackball);
|
||||
const renderer = Renderer.create(webgl, p.renderer);
|
||||
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
|
||||
const handleHelper = new HandleHelper(webgl, p.handle);
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
|
||||
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, {
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, handleHelper, {
|
||||
cameraHelper: p.camera.helper
|
||||
});
|
||||
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5);
|
||||
const pickPass = new PickPass(webgl, renderer, scene, camera, handleHelper, 0.5);
|
||||
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing);
|
||||
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample);
|
||||
|
||||
@@ -205,6 +208,7 @@ namespace Canvas3D {
|
||||
function getLoci(pickingId: PickingId) {
|
||||
let loci: Loci = EmptyLoci;
|
||||
let repr: Representation.Any = Representation.Empty;
|
||||
loci = handleHelper.getLoci(pickingId);
|
||||
reprRenderObjects.forEach((_, _repr) => {
|
||||
const _loci = _repr.getLoci(pickingId);
|
||||
if (!isEmptyLoci(_loci)) {
|
||||
@@ -224,10 +228,12 @@ namespace Canvas3D {
|
||||
if (repr) {
|
||||
changed = repr.mark(loci, action);
|
||||
} else {
|
||||
changed = handleHelper.mark(loci, action);
|
||||
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
|
||||
}
|
||||
if (changed) {
|
||||
scene.update(void 0, true);
|
||||
handleHelper.scene.update(void 0, true);
|
||||
const prevPickDirty = pickPass.pickDirty;
|
||||
draw(true);
|
||||
pickPass.pickDirty = prevPickDirty; // marking does not change picking buffers
|
||||
@@ -259,6 +265,7 @@ namespace Canvas3D {
|
||||
}
|
||||
|
||||
let forceNextDraw = false;
|
||||
let forceDrawAfterAllCommited = false;
|
||||
let currentTime = 0;
|
||||
|
||||
function draw(force?: boolean) {
|
||||
@@ -294,7 +301,13 @@ namespace Canvas3D {
|
||||
function commit(isSynchronous: boolean = false) {
|
||||
const allCommited = commitScene(isSynchronous);
|
||||
// Only reset the camera after the full scene has been commited.
|
||||
if (allCommited) resolveCameraReset();
|
||||
if (allCommited) {
|
||||
resolveCameraReset();
|
||||
if (forceDrawAfterAllCommited) {
|
||||
draw(true);
|
||||
forceDrawAfterAllCommited = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCameraReset() {
|
||||
@@ -356,10 +369,19 @@ namespace Canvas3D {
|
||||
|
||||
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
|
||||
reprCount.next(reprRenderObjects.size);
|
||||
if (isDebugMode) consoleStats();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function consoleStats() {
|
||||
console.table(scene.renderables.map(r => ({
|
||||
drawCount: r.values.drawCount.ref.value,
|
||||
instanceCount: r.values.instanceCount.ref.value,
|
||||
materialId: r.materialId,
|
||||
})));
|
||||
}
|
||||
|
||||
function add(repr: Representation.Any) {
|
||||
registerAutoUpdate(repr);
|
||||
|
||||
@@ -378,6 +400,8 @@ namespace Canvas3D {
|
||||
reprRenderObjects.set(repr, newRO);
|
||||
|
||||
scene.update(repr.renderObjects, false);
|
||||
forceDrawAfterAllCommited = true;
|
||||
if (isDebugMode) consoleStats();
|
||||
}
|
||||
|
||||
function remove(repr: Representation.Any) {
|
||||
@@ -388,6 +412,8 @@ namespace Canvas3D {
|
||||
renderObjects.forEach(o => scene.remove(o));
|
||||
reprRenderObjects.delete(repr);
|
||||
scene.update(repr.renderObjects, false, true);
|
||||
forceDrawAfterAllCommited = true;
|
||||
if (isDebugMode) consoleStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +454,8 @@ namespace Canvas3D {
|
||||
multiSample: { ...multiSample.props },
|
||||
renderer: { ...renderer.props },
|
||||
trackball: { ...controls.props },
|
||||
debug: { ...debugHelper.props }
|
||||
debug: { ...debugHelper.props },
|
||||
handle: { ...handleHelper.props },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -452,6 +479,7 @@ namespace Canvas3D {
|
||||
} else {
|
||||
scene.update(void 0, !!keepSphere);
|
||||
}
|
||||
forceDrawAfterAllCommited = true;
|
||||
},
|
||||
clear: () => {
|
||||
reprUpdatedSubscriptions.forEach(v => v.unsubscribe());
|
||||
@@ -471,6 +499,7 @@ namespace Canvas3D {
|
||||
if (scene.syncVisibility()) {
|
||||
if (debugHelper.isEnabled) debugHelper.update();
|
||||
}
|
||||
requestDraw(true);
|
||||
},
|
||||
|
||||
// draw,
|
||||
@@ -535,11 +564,12 @@ namespace Canvas3D {
|
||||
if (props.renderer) renderer.setProps(props.renderer);
|
||||
if (props.trackball) controls.setProps(props.trackball);
|
||||
if (props.debug) debugHelper.setProps(props.debug);
|
||||
if (props.handle) handleHelper.setProps(props.handle);
|
||||
|
||||
requestDraw(true);
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props);
|
||||
return new ImagePass(webgl, renderer, scene, camera, debugHelper, handleHelper, props);
|
||||
},
|
||||
|
||||
get props() {
|
||||
@@ -563,7 +593,8 @@ namespace Canvas3D {
|
||||
multiSample: { ...multiSample.props },
|
||||
renderer: { ...renderer.props },
|
||||
trackball: { ...controls.props },
|
||||
debug: { ...debugHelper.props }
|
||||
debug: { ...debugHelper.props },
|
||||
handle: { ...handleHelper.props },
|
||||
};
|
||||
},
|
||||
get input() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
208
src/mol-canvas3d/helper/handle-helper.ts
Normal file
208
src/mol-canvas3d/helper/handle-helper.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra';
|
||||
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
|
||||
import { GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import produce from 'immer';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { Camera } from '../camera';
|
||||
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
|
||||
import { Visual } from '../../mol-repr/visual';
|
||||
import { Interval } from '../../mol-data/int';
|
||||
|
||||
const HandleParams = {
|
||||
...Mesh.Params,
|
||||
alpha: { ...Mesh.Params.alpha, defaultValue: 1 },
|
||||
ignoreLight: { ...Mesh.Params.ignoreLight, defaultValue: true },
|
||||
colorX: PD.Color(ColorNames.red, { isEssential: true }),
|
||||
colorY: PD.Color(ColorNames.green, { isEssential: true }),
|
||||
colorZ: PD.Color(ColorNames.blue, { isEssential: true }),
|
||||
scale: PD.Numeric(0.33, { min: 0.1, max: 2, step: 0.1 }, { isEssential: true }),
|
||||
};
|
||||
type HandleParams = typeof HandleParams
|
||||
type HandleProps = PD.Values<HandleParams>
|
||||
|
||||
export const HandleHelperParams = {
|
||||
handle: PD.MappedStatic('off', {
|
||||
on: PD.Group(HandleParams),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Show handle tool' }),
|
||||
};
|
||||
export type HandleHelperParams = typeof HandleHelperParams
|
||||
export type HandleHelperProps = PD.Values<HandleHelperParams>
|
||||
|
||||
export class HandleHelper {
|
||||
scene: Scene
|
||||
props: HandleHelperProps = {
|
||||
handle: { name: 'off', params: {} }
|
||||
}
|
||||
|
||||
private renderObject: GraphicsRenderObject | undefined
|
||||
|
||||
private _transform = Mat4();
|
||||
getBoundingSphere(out: Sphere3D, instanceId: number) {
|
||||
if (this.renderObject) {
|
||||
Sphere3D.copy(out, this.renderObject.values.invariantBoundingSphere.ref.value);
|
||||
Mat4.fromArray(this._transform, this.renderObject.values.aTransform.ref.value, instanceId * 16);
|
||||
Sphere3D.transform(out, out, this._transform);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
setProps(props: Partial<HandleHelperProps>) {
|
||||
this.props = produce(this.props, p => {
|
||||
if (props.handle !== undefined) {
|
||||
p.handle.name = props.handle.name;
|
||||
if (props.handle.name === 'on') {
|
||||
this.scene.clear();
|
||||
const params = { ...props.handle.params, scale: props.handle.params.scale * this.webgl.pixelRatio };
|
||||
this.renderObject = createHandleRenderObject(params);
|
||||
this.scene.add(this.renderObject);
|
||||
this.scene.commit();
|
||||
|
||||
p.handle.params = { ...props.handle.params };
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get isEnabled() {
|
||||
return this.props.handle.name === 'on';
|
||||
}
|
||||
|
||||
// TODO could be a lists of position/rotation if we want to show more than one handle tool,
|
||||
// they would be distingishable by their instanceId
|
||||
update(camera: Camera, position: Vec3, rotation: Mat3) {
|
||||
if (!this.renderObject) return;
|
||||
|
||||
Mat4.setTranslation(this.renderObject.values.aTransform.ref.value as unknown as Mat4, position);
|
||||
Mat4.fromMat3(this.renderObject.values.aTransform.ref.value as unknown as Mat4, rotation);
|
||||
|
||||
// TODO make invariant to camera scaling by adjusting renderObject transform
|
||||
|
||||
ValueCell.update(this.renderObject.values.aTransform, this.renderObject.values.aTransform.ref.value);
|
||||
this.scene.update([this.renderObject], true);
|
||||
}
|
||||
|
||||
getLoci(pickingId: PickingId) {
|
||||
const { objectId, groupId, instanceId } = pickingId;
|
||||
if (!this.renderObject || objectId !== this.renderObject.id) return EmptyLoci;
|
||||
return HandleLoci(this, groupId, instanceId);
|
||||
}
|
||||
|
||||
private eachGroup = (loci: Loci, apply: (interval: Interval) => boolean): boolean => {
|
||||
if (!this.renderObject) return false;
|
||||
if (!isHandleLoci(loci)) return false;
|
||||
let changed = false;
|
||||
const groupCount = this.renderObject.values.uGroupCount.ref.value;
|
||||
const { elements } = loci;
|
||||
for (const { groupId, instanceId } of elements) {
|
||||
const idx = instanceId * groupCount + groupId;
|
||||
if (apply(Interval.ofSingleton(idx))) changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
mark(loci: Loci, action: MarkerAction) {
|
||||
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
|
||||
if (!isHandleLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, props: Partial<HandleHelperProps> = {}) {
|
||||
this.scene = Scene.create(webgl);
|
||||
this.setProps(props);
|
||||
}
|
||||
}
|
||||
|
||||
function createHandleMesh(scale: number, mesh?: Mesh) {
|
||||
const state = MeshBuilder.createState(512, 256, mesh);
|
||||
const radius = 0.05 * scale;
|
||||
const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
|
||||
const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
|
||||
const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
|
||||
|
||||
state.currentGroup = HandleGroup.TranslateScreenXY;
|
||||
addSphere(state, Vec3.origin, radius * 3, 2);
|
||||
|
||||
state.currentGroup = HandleGroup.TranslateObjectX;
|
||||
addSphere(state, x, radius, 2);
|
||||
addCylinder(state, Vec3.origin, x, 1, cylinderProps);
|
||||
|
||||
state.currentGroup = HandleGroup.TranslateObjectY;
|
||||
addSphere(state, y, radius, 2);
|
||||
addCylinder(state, Vec3.origin, y, 1, cylinderProps);
|
||||
|
||||
state.currentGroup = HandleGroup.TranslateObjectZ;
|
||||
addSphere(state, z, radius, 2);
|
||||
addCylinder(state, Vec3.origin, z, 1, cylinderProps);
|
||||
|
||||
// TODO add more helper geometries for the other HandleGroup options
|
||||
// TODO add props to create subset of geometries
|
||||
|
||||
return MeshBuilder.getMesh(state);
|
||||
}
|
||||
|
||||
export const HandleGroup = {
|
||||
None: 0,
|
||||
TranslateScreenXY: 1,
|
||||
// TranslateScreenZ: 2,
|
||||
TranslateObjectX: 3,
|
||||
TranslateObjectY: 4,
|
||||
TranslateObjectZ: 5,
|
||||
// TranslateObjectXY: 6,
|
||||
// TranslateObjectXZ: 7,
|
||||
// TranslateObjectYZ: 8,
|
||||
|
||||
// RotateScreenZ: 9,
|
||||
// RotateObjectX: 10,
|
||||
// RotateObjectY: 11,
|
||||
// RotateObjectZ: 12,
|
||||
} as const;
|
||||
|
||||
function HandleLoci(handleHelper: HandleHelper, groupId: number, instanceId: number) {
|
||||
return DataLoci('handle', handleHelper, [{ groupId, instanceId }],
|
||||
(boundingSphere: Sphere3D) => handleHelper.getBoundingSphere(boundingSphere, instanceId),
|
||||
() => `Handle Helper | Group Id ${groupId} | Instance Id ${instanceId}`);
|
||||
}
|
||||
export type HandleLoci = ReturnType<typeof HandleLoci>
|
||||
export function isHandleLoci(x: Loci): x is HandleLoci {
|
||||
return x.kind === 'data-loci' && x.tag === 'handle';
|
||||
}
|
||||
|
||||
function getHandleShape(props: HandleProps, shape?: Shape<Mesh>) {
|
||||
const scale = 10 * props.scale;
|
||||
const mesh = createHandleMesh(scale, shape?.geometry);
|
||||
mesh.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale + scale / 4));
|
||||
const getColor = (groupId: number) => {
|
||||
switch (groupId) {
|
||||
case HandleGroup.TranslateObjectX: return props.colorX;
|
||||
case HandleGroup.TranslateObjectY: return props.colorY;
|
||||
case HandleGroup.TranslateObjectZ: return props.colorZ;
|
||||
default: return ColorNames.grey;
|
||||
}
|
||||
};
|
||||
return Shape.create('handle', {}, mesh, getColor, () => 1, () => '');
|
||||
}
|
||||
|
||||
function createHandleRenderObject(props: HandleProps) {
|
||||
const shape = getHandleShape(props);
|
||||
return Shape.createRenderObject(shape, props);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera } from '../camera';
|
||||
import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { HandleHelper } from '../helper/handle-helper';
|
||||
|
||||
export const DrawPassParams = {
|
||||
cameraHelper: PD.Group(CameraHelperParams)
|
||||
@@ -29,7 +30,7 @@ export class DrawPass {
|
||||
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, props: Partial<DrawPassProps> = {}) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, private handleHelper: HandleHelper, props: Partial<DrawPassProps> = {}) {
|
||||
const { gl, extensions, resources } = webgl;
|
||||
const width = gl.drawingBufferWidth;
|
||||
const height = gl.drawingBufferHeight;
|
||||
@@ -89,12 +90,15 @@ export class DrawPass {
|
||||
}
|
||||
|
||||
private renderInternal(variant: 'color' | 'depth', transparentBackground: boolean) {
|
||||
const { renderer, scene, camera, debugHelper, cameraHelper } = this;
|
||||
const { renderer, scene, camera, debugHelper, cameraHelper, handleHelper } = this;
|
||||
renderer.render(scene, camera, variant, true, transparentBackground);
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility();
|
||||
renderer.render(debugHelper.scene, camera, variant, false, transparentBackground);
|
||||
}
|
||||
if (handleHelper.isEnabled) {
|
||||
renderer.render(handleHelper.scene, camera, variant, false, transparentBackground);
|
||||
}
|
||||
if (cameraHelper.isEnabled) {
|
||||
cameraHelper.update(camera);
|
||||
renderer.render(cameraHelper.scene, cameraHelper.camera, variant, false, transparentBackground);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { PostprocessingPass, PostprocessingParams } from './postprocessing';
|
||||
import { MultiSamplePass, MultiSampleParams } from './multi-sample';
|
||||
import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
import { HandleHelper } from '../helper/handle-helper';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
@@ -40,12 +41,12 @@ export class ImagePass {
|
||||
get width() { return this._width; }
|
||||
get height() { return this._height; }
|
||||
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, handleHelper: HandleHelper, props: Partial<ImageProps>) {
|
||||
const p = { ...PD.getDefaultValues(ImageParams), ...props };
|
||||
|
||||
this._transparentBackground = p.transparentBackground;
|
||||
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, p.drawPass);
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, handleHelper, p.drawPass);
|
||||
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing);
|
||||
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import Scene from '../../mol-gl/scene';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
import { Camera } from '../camera';
|
||||
import { HandleHelper } from '../helper/handle-helper';
|
||||
|
||||
export class PickPass {
|
||||
pickDirty = true
|
||||
@@ -27,7 +28,7 @@ export class PickPass {
|
||||
private pickWidth: number
|
||||
private pickHeight: number
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number) {
|
||||
const { gl } = webgl;
|
||||
const width = gl.drawingBufferWidth;
|
||||
const height = gl.drawingBufferHeight;
|
||||
@@ -65,14 +66,18 @@ export class PickPass {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { renderer, scene, camera } = this;
|
||||
const { renderer, scene, camera, handleHelper: { scene: handleScene } } = this;
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickObject', true, false);
|
||||
renderer.render(handleScene, camera, 'pickObject', false, false);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickInstance', true, false);
|
||||
renderer.render(handleScene, camera, 'pickInstance', false, false);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickGroup', true, false);
|
||||
renderer.render(handleScene, camera, 'pickGroup', false, false);
|
||||
|
||||
this.pickDirty = false;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const PostprocessingParams = {
|
||||
outline: PD.MappedStatic('off', {
|
||||
on: PD.Group({
|
||||
scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
|
||||
threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
|
||||
threshold: PD.Numeric(0.8, { min: 0, max: 5, step: 0.01 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Draw outline around 3D objects' })
|
||||
|
||||
@@ -72,15 +72,18 @@ export namespace BaseGeometry {
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
const opaque = props.alpha === undefined ? true : props.alpha === 1;
|
||||
return {
|
||||
visible: true,
|
||||
alphaFactor: 1,
|
||||
pickable: true,
|
||||
opaque: props.alpha === undefined ? true : props.alpha === 1
|
||||
opaque,
|
||||
writeDepth: opaque,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
state.opaque = props.alpha * state.alphaFactor >= 1;
|
||||
state.writeDepth = state.opaque;
|
||||
}
|
||||
}
|
||||
67
src/mol-geo/geometry/clipping-data.ts
Normal file
67
src/mol-geo/geometry/clipping-data.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
|
||||
export type ClippingData = {
|
||||
dClipObjectCount: ValueCell<number>,
|
||||
dClipVariant: ValueCell<string>,
|
||||
|
||||
tClipping: ValueCell<TextureImage<Uint8Array>>
|
||||
uClippingTexDim: ValueCell<Vec2>
|
||||
dClipping: ValueCell<boolean>,
|
||||
}
|
||||
|
||||
export function applyClippingGroups(array: Uint8Array, start: number, end: number, groups: Clipping.Groups) {
|
||||
for (let i = start; i < end; ++i) {
|
||||
array[i] = groups;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function clearClipping(array: Uint8Array, start: number, end: number) {
|
||||
array.fill(0, start, end);
|
||||
}
|
||||
|
||||
export function createClipping(count: number, clippingData?: ClippingData): ClippingData {
|
||||
const clipping = createTextureImage(Math.max(1, count), 1, Uint8Array, clippingData && clippingData.tClipping.ref.value.array);
|
||||
if (clippingData) {
|
||||
ValueCell.update(clippingData.tClipping, clipping);
|
||||
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(clipping.width, clipping.height));
|
||||
ValueCell.update(clippingData.dClipping, count > 0);
|
||||
return clippingData;
|
||||
} else {
|
||||
return {
|
||||
dClipObjectCount: ValueCell.create(0),
|
||||
dClipVariant: ValueCell.create('instance'),
|
||||
|
||||
tClipping: ValueCell.create(clipping),
|
||||
uClippingTexDim: ValueCell.create(Vec2.create(clipping.width, clipping.height)),
|
||||
dClipping: ValueCell.create(count > 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const emptyClippingTexture = { array: new Uint8Array(1), width: 1, height: 1 };
|
||||
export function createEmptyClipping(clippingData?: ClippingData): ClippingData {
|
||||
if (clippingData) {
|
||||
ValueCell.update(clippingData.tClipping, emptyClippingTexture);
|
||||
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(1, 1));
|
||||
return clippingData;
|
||||
} else {
|
||||
return {
|
||||
dClipObjectCount: ValueCell.create(0),
|
||||
dClipVariant: ValueCell.create('instance'),
|
||||
|
||||
tClipping: ValueCell.create(emptyClippingTexture),
|
||||
uClippingTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dClipping: ValueCell.create(false),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Mat4, Vec2, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Mat4, Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
@@ -26,6 +26,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
const VolumeBox = Box();
|
||||
const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][];
|
||||
@@ -140,6 +141,7 @@ export namespace DirectVolume {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount };
|
||||
|
||||
@@ -156,6 +158,7 @@ export namespace DirectVolume {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
|
||||
@@ -163,6 +166,7 @@ export namespace DirectVolume {
|
||||
elements: ValueCell.create(VolumeBox.indices as Uint32Array),
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
|
||||
uIsoValue: ValueCell.create(props.isoValueNorm),
|
||||
uBboxMin: bboxMin,
|
||||
@@ -204,6 +208,7 @@ export namespace DirectVolume {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ 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 { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
@@ -23,6 +23,7 @@ import { TransformData } from '../transform-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { ImageValues } from '../../../mol-gl/renderable/image';
|
||||
import { fillSerial } from '../../../mol-util/array';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
const QuadIndices = new Uint32Array([
|
||||
0, 1, 2,
|
||||
@@ -98,8 +99,7 @@ namespace Image {
|
||||
return image;
|
||||
}
|
||||
|
||||
function update(imageTexture: TextureImage<Uint8Array | Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
|
||||
|
||||
function update(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
|
||||
const width = imageTexture.width;
|
||||
const height = imageTexture.height;
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Image {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
interpolation: PD.Select('bspline', PD.objectToOptions(InterpolationTypes), { isEssential: true }),
|
||||
interpolation: PD.Select('bspline', PD.objectToOptions(InterpolationTypes)),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -138,6 +138,7 @@ namespace Image {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: QuadIndices.length, groupCount, instanceCount };
|
||||
|
||||
@@ -149,6 +150,7 @@ namespace Image {
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
|
||||
@@ -160,6 +162,7 @@ namespace Image {
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(4))),
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
|
||||
dInterpolation: ValueCell.create(props.interpolation),
|
||||
|
||||
@@ -176,7 +179,7 @@ namespace Image {
|
||||
}
|
||||
|
||||
function updateValues(values: ImageValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.uAlpha, props.alpha);
|
||||
BaseGeometry.updateValues(values, props);
|
||||
ValueCell.updateIfChanged(values.dInterpolation, props.interpolation);
|
||||
}
|
||||
|
||||
@@ -189,6 +192,7 @@ namespace Image {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra';
|
||||
import { Mat4, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
@@ -25,6 +25,7 @@ import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
/** Wide line */
|
||||
export interface Lines {
|
||||
@@ -184,6 +185,7 @@ export namespace Lines {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount };
|
||||
|
||||
@@ -198,11 +200,13 @@ export namespace Lines {
|
||||
elements: lines.indexBuffer,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
...color,
|
||||
...size,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -234,6 +238,7 @@ export namespace Lines {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
|
||||
import { Vec3, Mat4, Mat3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
@@ -23,6 +23,7 @@ import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
export interface Mesh {
|
||||
readonly kind: 'mesh',
|
||||
@@ -355,6 +356,7 @@ export namespace Mesh {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount };
|
||||
|
||||
@@ -368,10 +370,12 @@ export namespace Mesh {
|
||||
elements: mesh.indexBuffer,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
...color,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -405,6 +409,7 @@ export namespace Mesh {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ export type OverpaintData = {
|
||||
dOverpaint: ValueCell<boolean>,
|
||||
}
|
||||
|
||||
export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color, alpha: number) {
|
||||
export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
|
||||
for (let i = start; i < end; ++i) {
|
||||
Color.toArray(color, array, i * 4);
|
||||
array[i * 4 + 3] = alpha * 255;
|
||||
array[i * 4 + 3] = 255;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra';
|
||||
import { Mat4, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
@@ -24,6 +24,7 @@ import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
/** Point cloud */
|
||||
export interface Points {
|
||||
@@ -143,6 +144,7 @@ export namespace Points {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: points.pointCount, groupCount, instanceCount };
|
||||
|
||||
@@ -154,11 +156,13 @@ export namespace Points {
|
||||
aGroup: points.groupBuffer,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
...color,
|
||||
...size,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -192,6 +196,7 @@ export namespace Points {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,5 +212,6 @@ export namespace Points {
|
||||
!props.pointFilledCircle ||
|
||||
(props.pointFilledCircle && props.pointEdgeBleach === 0)
|
||||
);
|
||||
state.writeDepth = state.opaque;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { Vec4 } from '../../../mol-math/linear-algebra';
|
||||
|
||||
export interface Spheres {
|
||||
readonly kind: 'spheres',
|
||||
@@ -147,6 +149,7 @@ export namespace Spheres {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount };
|
||||
|
||||
@@ -161,11 +164,13 @@ export namespace Spheres {
|
||||
elements: spheres.indexBuffer,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
...color,
|
||||
...size,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
padding: ValueCell.create(padding),
|
||||
@@ -200,6 +205,7 @@ export namespace Spheres {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
ValueCell.update(values.padding, padding);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { TextureImage, createTextureImage, calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { TextValues } from '../../../mol-gl/renderable/text';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Vec3, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { FontAtlasParams } from './font-atlas';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { clamp } from '../../../mol-math/interpolate';
|
||||
@@ -28,6 +28,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
type TextAttachment = (
|
||||
'bottom-left' | 'bottom-center' | 'bottom-right' |
|
||||
@@ -195,6 +196,7 @@ export namespace Text {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount };
|
||||
|
||||
@@ -210,11 +212,13 @@ export namespace Text {
|
||||
elements: text.indexBuffer,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
...color,
|
||||
...size,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
aTexCoord: text.tcoordBuffer,
|
||||
@@ -269,6 +273,7 @@ export namespace Text {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
ValueCell.update(values.padding, padding);
|
||||
}
|
||||
@@ -283,6 +288,7 @@ export namespace Text {
|
||||
BaseGeometry.updateRenderableState(state, props);
|
||||
state.pickable = false;
|
||||
state.opaque = false;
|
||||
state.writeDepth = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -20,8 +20,9 @@ import { createEmptyTransparency } from '../transparency-data';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { Vec2 } from '../../../mol-math/linear-algebra';
|
||||
import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { fillSerial } from '../../../mol-util/array';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
|
||||
export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
@@ -93,6 +94,7 @@ export namespace TextureMesh {
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const counts = { drawCount: textureMesh.vertexCount, groupCount, instanceCount };
|
||||
|
||||
@@ -107,11 +109,13 @@ export namespace TextureMesh {
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))),
|
||||
boundingSphere: ValueCell.create(transformBoundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(Sphere3D.clone(textureMesh.boundingSphere)),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(textureMesh.boundingSphere)),
|
||||
|
||||
...color,
|
||||
...marker,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
...transform,
|
||||
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
@@ -149,6 +153,7 @@ export namespace TextureMesh {
|
||||
}
|
||||
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Transparency } from '../../mol-theme/transparency';
|
||||
|
||||
export type TransparencyData = {
|
||||
tTransparency: ValueCell<TextureImage<Uint8Array>>
|
||||
@@ -27,20 +26,19 @@ export function clearTransparency(array: Uint8Array, start: number, end: number)
|
||||
array.fill(0, start, end);
|
||||
}
|
||||
|
||||
export function createTransparency(count: number, variant: Transparency.Variant, transparencyData?: TransparencyData): TransparencyData {
|
||||
export function createTransparency(count: number, transparencyData?: TransparencyData): TransparencyData {
|
||||
const transparency = createTextureImage(Math.max(1, count), 1, Uint8Array, transparencyData && transparencyData.tTransparency.ref.value.array);
|
||||
if (transparencyData) {
|
||||
ValueCell.update(transparencyData.tTransparency, transparency);
|
||||
ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height));
|
||||
ValueCell.update(transparencyData.dTransparency, count > 0);
|
||||
ValueCell.update(transparencyData.dTransparencyVariant, variant);
|
||||
return transparencyData;
|
||||
} else {
|
||||
return {
|
||||
tTransparency: ValueCell.create(transparency),
|
||||
uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
|
||||
dTransparency: ValueCell.create(count > 0),
|
||||
dTransparencyVariant: ValueCell.create(variant),
|
||||
dTransparencyVariant: ValueCell.create('single'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { createGl } from './gl.shim';
|
||||
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { Vec3, Mat4, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
|
||||
import Renderer from '../renderer';
|
||||
@@ -24,6 +24,7 @@ import { Color } from '../../mol-util/color';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { createEmptyOverpaint } from '../../mol-geo/geometry/overpaint-data';
|
||||
import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-data';
|
||||
import { createEmptyClipping } from '../../mol-geo/geometry/clipping-data';
|
||||
|
||||
function createRenderer(gl: WebGLRenderingContext) {
|
||||
const ctx = createContext(gl);
|
||||
@@ -43,6 +44,7 @@ function createPoints() {
|
||||
const marker = createEmptyMarkers();
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const clipping = createEmptyClipping();
|
||||
|
||||
const aTransform = ValueCell.create(new Float32Array(16));
|
||||
const m4 = Mat4.identity();
|
||||
@@ -63,10 +65,12 @@ function createPoints() {
|
||||
...size,
|
||||
...overpaint,
|
||||
...transparency,
|
||||
...clipping,
|
||||
|
||||
uAlpha: ValueCell.create(1.0),
|
||||
uInstanceCount: ValueCell.create(1),
|
||||
uGroupCount: ValueCell.create(3),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere.ref.value)),
|
||||
|
||||
alpha: ValueCell.create(1.0),
|
||||
drawCount: ValueCell.create(3),
|
||||
@@ -86,7 +90,8 @@ function createPoints() {
|
||||
visible: true,
|
||||
alphaFactor: 1,
|
||||
pickable: true,
|
||||
opaque: true
|
||||
opaque: true,
|
||||
writeDepth: true
|
||||
};
|
||||
|
||||
return createRenderObject('points', values, state, -1);
|
||||
@@ -123,7 +128,7 @@ describe('renderer', () => {
|
||||
scene.add(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(4);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -29,8 +29,15 @@ export const DirectVolumeSchema = {
|
||||
dTransparency: DefineSpec('boolean'),
|
||||
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
|
||||
|
||||
dClipObjectCount: DefineSpec('number'),
|
||||
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
|
||||
uClippingTexDim: UniformSpec('v2'),
|
||||
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dClipping: DefineSpec('boolean'),
|
||||
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
uInvariantBoundingSphere: UniformSpec('v4'),
|
||||
|
||||
aInstance: AttributeSpec('float32', 1, 1),
|
||||
aTransform: AttributeSpec('float32', 16, 1),
|
||||
@@ -73,7 +80,6 @@ export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: Di
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0),
|
||||
};
|
||||
const shaderCode = DirectVolumeShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -33,7 +33,6 @@ export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValu
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...ImageSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0),
|
||||
};
|
||||
const shaderCode = ImageShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -29,7 +29,6 @@ export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValu
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = LinesShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -28,7 +28,6 @@ export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = MeshShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -26,7 +26,6 @@ export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsVa
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = PointsShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { AttributeItemSize, ElementsKind, AttributeValues, AttributeKind } from '../webgl/buffer';
|
||||
import { UniformKind, UniformValues } from '../webgl/uniform';
|
||||
import { AttributeItemSize, ElementsKind, AttributeValues, AttributeKind, DataTypeArrayType } from '../webgl/buffer';
|
||||
import { UniformKind, UniformValues, UniformKindValue } from '../webgl/uniform';
|
||||
import { DefineKind, DefineValues } from '../shader-code';
|
||||
import { Vec2, Vec3, Vec4, Mat3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, TextureVolume } from './util';
|
||||
import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, Texture } from '../webgl/texture';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, TextureKindValue } from '../webgl/texture';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
|
||||
export type ValueKindType = {
|
||||
'number': number
|
||||
'string': string
|
||||
'boolean': string
|
||||
'boolean': boolean
|
||||
'any': any
|
||||
|
||||
'm4': Mat4,
|
||||
@@ -27,40 +26,7 @@ export type ValueKind = keyof ValueKindType
|
||||
|
||||
//
|
||||
|
||||
export type KindValue = {
|
||||
'f': number
|
||||
'i': number
|
||||
'v2': Vec2
|
||||
'v3': Vec3
|
||||
'v4': Vec4
|
||||
'm3': Mat3
|
||||
'm4': Mat4
|
||||
't': number
|
||||
|
||||
'uint8': Uint8Array
|
||||
'int8': Int8Array
|
||||
'uint16': Uint16Array
|
||||
'int16': Int16Array
|
||||
'uint32': Uint32Array
|
||||
'int32': Int32Array
|
||||
'float32': Float32Array
|
||||
|
||||
'image-uint8': TextureImage<Uint8Array>
|
||||
'image-float32': TextureImage<Float32Array>
|
||||
'image-depth': TextureImage<Uint8Array> // TODO should be Uint32Array
|
||||
'volume-uint8': TextureVolume<Uint8Array>
|
||||
'volume-float32': TextureVolume<Float32Array>
|
||||
'texture': Texture
|
||||
'texture2d': Texture
|
||||
'texture3d': Texture
|
||||
|
||||
'number': number
|
||||
'string': string
|
||||
'boolean': boolean
|
||||
'any': any
|
||||
|
||||
'sphere': Sphere3D
|
||||
}
|
||||
export type KindValue = UniformKindValue & DataTypeArrayType & TextureKindValue & ValueKindType
|
||||
|
||||
export type Values<S extends RenderableSchema> = { readonly [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
|
||||
|
||||
@@ -163,6 +129,11 @@ export const GlobalUniformSchema = {
|
||||
|
||||
uTransparentBackground: UniformSpec('i'),
|
||||
|
||||
uClipObjectType: UniformSpec('i[]'),
|
||||
uClipObjectPosition: UniformSpec('v3[]'),
|
||||
uClipObjectRotation: UniformSpec('v4[]'),
|
||||
uClipObjectScale: UniformSpec('v3[]'),
|
||||
|
||||
// all the following could in principle be per object
|
||||
// as a kind of 'material' parameter set
|
||||
// would need to test performance implications
|
||||
@@ -187,7 +158,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> }
|
||||
@@ -229,21 +199,31 @@ export type OverpaintSchema = typeof OverpaintSchema
|
||||
export type OverpaintValues = Values<OverpaintSchema>
|
||||
|
||||
export const TransparencySchema = {
|
||||
// aTransparency: AttributeSpec('float32', 1, 0), // TODO
|
||||
uTransparencyTexDim: UniformSpec('v2'),
|
||||
tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dTransparency: DefineSpec('boolean'),
|
||||
// dTransparencyType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), // TODO
|
||||
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
|
||||
} as const;
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
|
||||
export const ClippingSchema = {
|
||||
dClipObjectCount: DefineSpec('number'),
|
||||
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
|
||||
|
||||
uClippingTexDim: UniformSpec('v2'),
|
||||
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dClipping: DefineSpec('boolean'),
|
||||
} as const;
|
||||
export type ClippingSchema = typeof ClippingSchema
|
||||
export type ClippingValues = Values<ClippingSchema>
|
||||
|
||||
export const BaseSchema = {
|
||||
...ColorSchema,
|
||||
...MarkerSchema,
|
||||
...OverpaintSchema,
|
||||
...TransparencySchema,
|
||||
...ClippingSchema,
|
||||
|
||||
aInstance: AttributeSpec('float32', 1, 1),
|
||||
aGroup: AttributeSpec('float32', 1, 0),
|
||||
@@ -259,6 +239,7 @@ export const BaseSchema = {
|
||||
uAlpha: UniformSpec('f', true),
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
uInvariantBoundingSphere: UniformSpec('v4'),
|
||||
|
||||
drawCount: ValueSpec('number'),
|
||||
instanceCount: ValueSpec('number'),
|
||||
@@ -273,7 +254,7 @@ export const BaseSchema = {
|
||||
/** additional per-instance transform, see aTransform */
|
||||
extraTransform: ValueSpec('float32'),
|
||||
|
||||
/** bounding sphere taking aTransform into account */
|
||||
/** bounding sphere taking aTransform into account and encompases all instances */
|
||||
boundingSphere: ValueSpec('sphere'),
|
||||
/** bounding sphere NOT taking aTransform into account */
|
||||
invariantBoundingSphere: ValueSpec('sphere'),
|
||||
|
||||
@@ -29,7 +29,6 @@ export function SpheresRenderable(ctx: WebGLContext, id: number, values: Spheres
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...SpheresSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = SpheresShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -38,7 +38,6 @@ export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = TextShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -31,7 +31,6 @@ export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: Tex
|
||||
const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextureMeshSchema };
|
||||
const internalValues: InternalValues = {
|
||||
uObjectId: ValueCell.create(id),
|
||||
uPickable: ValueCell.create(state.pickable ? 1 : 0)
|
||||
};
|
||||
const shaderCode = MeshShaderCode;
|
||||
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface TextureImage<T extends Uint8Array | Float32Array> {
|
||||
readonly array: T
|
||||
readonly width: number
|
||||
readonly height: number
|
||||
readonly flipY?: boolean
|
||||
}
|
||||
|
||||
export interface TextureVolume<T extends Uint8Array | Float32Array> {
|
||||
|
||||
@@ -9,13 +9,16 @@ import { Camera } from '../mol-canvas3d/camera';
|
||||
|
||||
import Scene from './scene';
|
||||
import { WebGLContext } from './webgl/context';
|
||||
import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
|
||||
import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra';
|
||||
import { Renderable } from './renderable';
|
||||
import { Color } from '../mol-util/color';
|
||||
import { ValueCell } from '../mol-util';
|
||||
import { ValueCell, deepEqual } from '../mol-util';
|
||||
import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
|
||||
import { GraphicsRenderVariant } from './webgl/render-item';
|
||||
import { ParamDefinition as PD } from '../mol-util/param-definition';
|
||||
import { Clipping } from '../mol-theme/clipping';
|
||||
import { stringToWords } from '../mol-util/string';
|
||||
import { Transparency } from '../mol-theme/transparency';
|
||||
|
||||
export interface RendererStats {
|
||||
programCount: number
|
||||
@@ -49,6 +52,7 @@ export const RendererParams = {
|
||||
|
||||
// the following are general 'material' parameters
|
||||
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
|
||||
transparencyVariant: PD.Select('single', PD.arrayToOptions<Transparency.Variant>(['single', 'multi'])),
|
||||
|
||||
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
|
||||
@@ -71,6 +75,19 @@ export const RendererParams = {
|
||||
metallic: PD.Group({}),
|
||||
plastic: PD.Group({}),
|
||||
}, { label: 'Lighting', description: 'Style in which the 3D scene is rendered/lighted' }),
|
||||
|
||||
clip: PD.Group({
|
||||
variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
|
||||
objects: PD.ObjectList({
|
||||
type: PD.Select('plane', PD.objectToOptions(Clipping.Type, t => stringToWords(t))),
|
||||
position: PD.Vec3(Vec3()),
|
||||
rotation: PD.Group({
|
||||
axis: PD.Vec3(Vec3.create(1, 0, 0)),
|
||||
angle: PD.Numeric(0, { min: -180, max: 180, step: 0.1 }),
|
||||
}, { isExpanded: true }),
|
||||
scale: PD.Vec3(Vec3.create(1, 1, 1)),
|
||||
}, o => stringToWords(o.type))
|
||||
})
|
||||
};
|
||||
export type RendererProps = PD.Values<typeof RendererParams>
|
||||
|
||||
@@ -106,11 +123,44 @@ function getStyle(props: RendererProps['style']) {
|
||||
}
|
||||
}
|
||||
|
||||
type Clip = {
|
||||
variant: Clipping.Variant
|
||||
objects: {
|
||||
count: number
|
||||
type: number[]
|
||||
position: number[]
|
||||
rotation: number[]
|
||||
scale: number[]
|
||||
}
|
||||
}
|
||||
|
||||
const tmpQuat = Quat();
|
||||
function getClip(props: RendererProps['clip'], clip?: Clip): Clip {
|
||||
const { type, position, rotation, scale } = clip?.objects || {
|
||||
type: (new Array(5)).fill(1),
|
||||
position: (new Array(5 * 3)).fill(0),
|
||||
rotation: (new Array(5 * 4)).fill(0),
|
||||
scale: (new Array(5 * 3)).fill(1),
|
||||
};
|
||||
for (let i = 0, il = props.objects.length; i < il; ++i) {
|
||||
const p = props.objects[i];
|
||||
type[i] = Clipping.Type[p.type];
|
||||
Vec3.toArray(p.position, position, i * 3);
|
||||
Quat.toArray(Quat.setAxisAngle(tmpQuat, p.rotation.axis, p.rotation.angle), rotation, i * 4);
|
||||
Vec3.toArray(p.scale, scale, i * 3);
|
||||
}
|
||||
return {
|
||||
variant: props.variant,
|
||||
objects: { count: props.objects.length, type, position, rotation, scale }
|
||||
};
|
||||
}
|
||||
|
||||
namespace Renderer {
|
||||
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
||||
const { gl, state, stats } = ctx;
|
||||
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
|
||||
const style = getStyle(p.style);
|
||||
const clip = getClip(p.clip);
|
||||
|
||||
const viewport = Viewport();
|
||||
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor);
|
||||
@@ -151,6 +201,11 @@ namespace Renderer {
|
||||
uFogColor: ValueCell.create(bgColor),
|
||||
uTransparentBackground: ValueCell.create(0),
|
||||
|
||||
uClipObjectType: ValueCell.create(clip.objects.type),
|
||||
uClipObjectPosition: ValueCell.create(clip.objects.position),
|
||||
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
|
||||
uClipObjectScale: ValueCell.create(clip.objects.scale),
|
||||
|
||||
// the following are general 'material' uniforms
|
||||
uLightIntensity: ValueCell.create(style.lightIntensity),
|
||||
uAmbientIntensity: ValueCell.create(style.ambientIntensity),
|
||||
@@ -173,47 +228,68 @@ namespace Renderer {
|
||||
let globalUniformsNeedUpdate = true;
|
||||
|
||||
const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => {
|
||||
if (!r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let definesNeedUpdate = false;
|
||||
if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
|
||||
ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
|
||||
definesNeedUpdate = true;
|
||||
}
|
||||
if (r.values.dClipVariant.ref.value !== clip.variant) {
|
||||
ValueCell.update(r.values.dClipVariant, clip.variant);
|
||||
definesNeedUpdate = true;
|
||||
}
|
||||
if (r.values.dTransparencyVariant.ref.value !== p.transparencyVariant) {
|
||||
ValueCell.update(r.values.dTransparencyVariant, p.transparencyVariant);
|
||||
definesNeedUpdate = true;
|
||||
}
|
||||
if (definesNeedUpdate) r.update();
|
||||
|
||||
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 +321,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 +344,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) {
|
||||
@@ -302,6 +379,9 @@ namespace Renderer {
|
||||
p.pickingAlphaThreshold = props.pickingAlphaThreshold;
|
||||
ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold);
|
||||
}
|
||||
if (props.transparencyVariant !== undefined && props.transparencyVariant !== p.transparencyVariant) {
|
||||
p.transparencyVariant = props.transparencyVariant;
|
||||
}
|
||||
|
||||
if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
|
||||
p.interiorDarkening = props.interiorDarkening;
|
||||
@@ -334,6 +414,15 @@ namespace Renderer {
|
||||
ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness);
|
||||
ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity);
|
||||
}
|
||||
|
||||
if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
|
||||
p.clip = props.clip;
|
||||
Object.assign(clip, getClip(props.clip, clip));
|
||||
ValueCell.update(globalUniforms.uClipObjectPosition, clip.objects.position);
|
||||
ValueCell.update(globalUniforms.uClipObjectRotation, clip.objects.rotation);
|
||||
ValueCell.update(globalUniforms.uClipObjectScale, clip.objects.scale);
|
||||
ValueCell.update(globalUniforms.uClipObjectType, clip.objects.type);
|
||||
}
|
||||
},
|
||||
setViewport: (x: number, y: number, width: number, height: number) => {
|
||||
gl.viewport(x, y, width, height);
|
||||
|
||||
@@ -50,9 +50,11 @@ function renderableSort(a: Renderable<RenderableValues & BaseValues>, b: Rendera
|
||||
const materialIdB = b.materialId;
|
||||
|
||||
if (drawProgramIdA !== drawProgramIdB) {
|
||||
return drawProgramIdA - drawProgramIdB; // sort by program id to minimize gl state changes
|
||||
// sort by program id to minimize gl state changes
|
||||
return drawProgramIdA - drawProgramIdB;
|
||||
} else if (materialIdA !== materialIdB) {
|
||||
return materialIdA - materialIdB; // sort by material id to minimize gl state changes
|
||||
// sort by material id to minimize gl state changes
|
||||
return materialIdA - materialIdB;
|
||||
} else {
|
||||
return a.id - b.id;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -34,6 +34,7 @@ import apply_fog from './shader/chunks/apply-fog.glsl';
|
||||
import apply_interior_color from './shader/chunks/apply-interior-color.glsl';
|
||||
import apply_light_color from './shader/chunks/apply-light-color.glsl';
|
||||
import apply_marker_color from './shader/chunks/apply-marker-color.glsl';
|
||||
import assign_clipping_varying from './shader/chunks/assign-clipping-varying.glsl';
|
||||
import assign_color_varying from './shader/chunks/assign-color-varying.glsl';
|
||||
import assign_group from './shader/chunks/assign-group.glsl';
|
||||
import assign_marker_varying from './shader/chunks/assign-marker-varying.glsl';
|
||||
@@ -41,8 +42,11 @@ import assign_material_color from './shader/chunks/assign-material-color.glsl';
|
||||
import assign_position from './shader/chunks/assign-position.glsl';
|
||||
import assign_size from './shader/chunks/assign-size.glsl';
|
||||
import check_picking_alpha from './shader/chunks/check-picking-alpha.glsl';
|
||||
import clip_instance from './shader/chunks/clip-instance.glsl';
|
||||
import clip_pixel from './shader/chunks/clip-pixel.glsl';
|
||||
import color_frag_params from './shader/chunks/color-frag-params.glsl';
|
||||
import color_vert_params from './shader/chunks/color-vert-params.glsl';
|
||||
import common_clip from './shader/chunks/common-clip.glsl';
|
||||
import common_frag_params from './shader/chunks/common-frag-params.glsl';
|
||||
import common_vert_params from './shader/chunks/common-vert-params.glsl';
|
||||
import common from './shader/chunks/common.glsl';
|
||||
@@ -59,6 +63,7 @@ const ShaderChunks: { [k: string]: string } = {
|
||||
apply_interior_color,
|
||||
apply_light_color,
|
||||
apply_marker_color,
|
||||
assign_clipping_varying,
|
||||
assign_color_varying,
|
||||
assign_group,
|
||||
assign_marker_varying,
|
||||
@@ -66,8 +71,11 @@ const ShaderChunks: { [k: string]: string } = {
|
||||
assign_position,
|
||||
assign_size,
|
||||
check_picking_alpha,
|
||||
clip_instance,
|
||||
clip_pixel,
|
||||
color_frag_params,
|
||||
color_vert_params,
|
||||
common_clip,
|
||||
common_frag_params,
|
||||
common_vert_params,
|
||||
common,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
export default `
|
||||
// only mark elements with an alpha above the picking threshold
|
||||
if (gl_FragColor.a >= uPickingAlphaThreshold) {
|
||||
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
|
||||
if (marker > 0.1) {
|
||||
if (intMod(marker, 2.0) > 0.1) {
|
||||
gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3);
|
||||
} else {
|
||||
gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3);
|
||||
}
|
||||
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
|
||||
if (marker > 0.1) {
|
||||
if (intMod(marker, 2.0) > 0.1) {
|
||||
gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3);
|
||||
} else {
|
||||
gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3);
|
||||
}
|
||||
}
|
||||
`;
|
||||
5
src/mol-gl/shader/chunks/assign-clipping-varying.glsl.ts
Normal file
5
src/mol-gl/shader/chunks/assign-clipping-varying.glsl.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default `
|
||||
#if dClipObjectCount != 0 && defined(dClipping)
|
||||
vClipping = readFromTexture(tClipping, aInstance * float(uGroupCount) + group, uClippingTexDim).a;
|
||||
#endif
|
||||
`;
|
||||
@@ -34,7 +34,7 @@ export default `
|
||||
if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
|
||||
#endif
|
||||
#elif defined(dRenderVariant_pick)
|
||||
vec4 material = uPickable == 1 ? vColor : vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
|
||||
vec4 material = vColor;
|
||||
#elif defined(dRenderVariant_depth)
|
||||
#ifdef enabledFragDepth
|
||||
vec4 material = packDepthToRGBA(gl_FragDepthEXT);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
export default `
|
||||
mat4 modelView = uView * uModel * aTransform;
|
||||
mat4 model = uModel * aTransform;
|
||||
mat4 modelView = uView * model;
|
||||
#ifdef dGeoTexture
|
||||
vec3 position = readFromTexture(tPositionGroup, aGroup, uGeoTexDim).xyz;
|
||||
#else
|
||||
vec3 position = aPosition;
|
||||
#endif
|
||||
vec4 mvPosition = modelView * vec4(position, 1.0);
|
||||
vec4 position4 = vec4(position, 1.0);
|
||||
vModelPosition = (model * position4).xyz; // for clipping in frag shader
|
||||
vec4 mvPosition = modelView * position4;
|
||||
vViewPosition = mvPosition.xyz;
|
||||
gl_Position = uProjection * mvPosition;
|
||||
`;
|
||||
13
src/mol-gl/shader/chunks/clip-instance.glsl.ts
Normal file
13
src/mol-gl/shader/chunks/clip-instance.glsl.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default `
|
||||
#if defined(dClipVariant_instance) && dClipObjectCount != 0
|
||||
int flag = 0;
|
||||
#if defined(dClipping)
|
||||
flag = int(floor(vClipping * 255.0 + 0.5));
|
||||
#endif
|
||||
|
||||
vec4 mCenter = uModel * aTransform * vec4(uInvariantBoundingSphere.xyz, 1.0);
|
||||
if (clipTest(vec4(mCenter.xyz, uInvariantBoundingSphere.w), flag))
|
||||
// move out of [ -w, +w ] to 'discard' in vert shader
|
||||
gl_Position.z = 2.0 * gl_Position.w;
|
||||
#endif
|
||||
`;
|
||||
12
src/mol-gl/shader/chunks/clip-pixel.glsl.ts
Normal file
12
src/mol-gl/shader/chunks/clip-pixel.glsl.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export default `
|
||||
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
|
||||
#if defined(dClipping)
|
||||
int clippingFlag = int(floor(vClipping * 255.0 + 0.5));
|
||||
#else
|
||||
int clippingFlag = 0;
|
||||
#endif
|
||||
|
||||
if (clipTest(vec4(vModelPosition, 0.0), clippingFlag))
|
||||
discard;
|
||||
#endif
|
||||
`;
|
||||
102
src/mol-gl/shader/chunks/common-clip.glsl.ts
Normal file
102
src/mol-gl/shader/chunks/common-clip.glsl.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Ludovic Autin <autin@scripps.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
export default `
|
||||
#if dClipObjectCount != 0
|
||||
vec3 quaternionTransform(vec4 q, vec3 v) {
|
||||
vec3 t = 2.0 * cross(q.xyz, v);
|
||||
return v + q.w * t + cross(q.xyz, t);
|
||||
}
|
||||
|
||||
vec4 computePlane(vec3 normal, vec3 inPoint) {
|
||||
return vec4(normalize(normal), -dot(normal, inPoint));
|
||||
}
|
||||
|
||||
float planeSD(vec4 plane, vec3 center) {
|
||||
return -dot(plane.xyz, center - plane.xyz * -plane.w);
|
||||
}
|
||||
|
||||
float sphereSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
|
||||
return (
|
||||
length(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position) / size) - 1.0
|
||||
) * min(min(size.x, size.y), size.z);
|
||||
}
|
||||
|
||||
float cubeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
|
||||
vec3 d = abs(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position)) - size;
|
||||
return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
|
||||
}
|
||||
|
||||
float cylinderSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
|
||||
vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position);
|
||||
|
||||
vec2 d = abs(vec2(length(t.xz), t.y)) - size.xy;
|
||||
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
|
||||
}
|
||||
|
||||
float infiniteConeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
|
||||
vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position);
|
||||
|
||||
float q = length(t.xy);
|
||||
return dot(size.xy, vec2(q, t.z));
|
||||
}
|
||||
|
||||
float getSignedDistance(vec3 center, int type, vec3 position, vec4 rotation, vec3 scale) {
|
||||
if (type == 1) {
|
||||
vec3 normal = quaternionTransform(rotation, vec3(0.0, 1.0, 0.0));
|
||||
vec4 plane = computePlane(normal, position);
|
||||
return planeSD(plane, center);
|
||||
} else if (type == 2) {
|
||||
return sphereSD(position, rotation, scale * 0.5, center);
|
||||
} else if (type == 3) {
|
||||
return cubeSD(position, rotation, scale * 0.5, center);
|
||||
} else if (type == 4) {
|
||||
return cylinderSD(position, rotation, scale * 0.5, center);
|
||||
} else if (type == 5) {
|
||||
return infiniteConeSD(position, rotation, scale * 0.5, center);
|
||||
} else {
|
||||
return 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
#if __VERSION__ != 300
|
||||
// 8-bit
|
||||
int bitwiseAnd(int a, int b) {
|
||||
int d = 128;
|
||||
int result = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (d <= 0) break;
|
||||
if (a >= d && b >= d) result += d;
|
||||
if (a >= d) a -= d;
|
||||
if (b >= d) b -= d;
|
||||
d /= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool hasBit(int mask, int bit) {
|
||||
return bitwiseAnd(mask, bit) == 0;
|
||||
}
|
||||
#else
|
||||
bool hasBit(int mask, int bit) {
|
||||
return (mask & bit) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0)
|
||||
bool clipTest(vec4 sphere, int flag) {
|
||||
for (int i = 0; i < dClipObjectCount; ++i) {
|
||||
if (flag == 0 || hasBit(flag, i + 1)) {
|
||||
// TODO take sphere radius into account?
|
||||
if (getSignedDistance(sphere.xyz, uClipObjectType[i], uClipObjectPosition[i], uClipObjectRotation[i], uClipObjectScale[i]) <= 0.0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
`;
|
||||
@@ -3,6 +3,21 @@ uniform int uObjectId;
|
||||
uniform int uInstanceCount;
|
||||
uniform int uGroupCount;
|
||||
|
||||
#if dClipObjectCount != 0
|
||||
uniform int uClipObjectType[dClipObjectCount];
|
||||
uniform vec3 uClipObjectPosition[dClipObjectCount];
|
||||
uniform vec4 uClipObjectRotation[dClipObjectCount];
|
||||
uniform vec3 uClipObjectScale[dClipObjectCount];
|
||||
|
||||
#if defined(dClipping)
|
||||
#if __VERSION__ != 300
|
||||
varying float vClipping;
|
||||
#else
|
||||
flat in float vClipping;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uniform vec3 uHighlightColor;
|
||||
uniform vec3 uSelectColor;
|
||||
#if __VERSION__ != 300
|
||||
@@ -11,6 +26,7 @@ uniform vec3 uSelectColor;
|
||||
flat in float vMarker;
|
||||
#endif
|
||||
|
||||
varying vec3 vModelPosition;
|
||||
varying vec3 vViewPosition;
|
||||
|
||||
uniform vec2 uViewOffset;
|
||||
@@ -21,7 +37,6 @@ uniform vec3 uFogColor;
|
||||
|
||||
uniform float uAlpha;
|
||||
uniform float uPickingAlphaThreshold;
|
||||
uniform int uPickable;
|
||||
uniform int uTransparentBackground;
|
||||
|
||||
uniform float uInteriorDarkening;
|
||||
|
||||
@@ -5,6 +5,24 @@ uniform vec3 uCameraPosition;
|
||||
uniform int uObjectId;
|
||||
uniform int uInstanceCount;
|
||||
uniform int uGroupCount;
|
||||
uniform vec4 uInvariantBoundingSphere;
|
||||
|
||||
#if dClipObjectCount != 0
|
||||
uniform int uClipObjectType[dClipObjectCount];
|
||||
uniform vec3 uClipObjectPosition[dClipObjectCount];
|
||||
uniform vec4 uClipObjectRotation[dClipObjectCount];
|
||||
uniform vec3 uClipObjectScale[dClipObjectCount];
|
||||
|
||||
#if defined(dClipping)
|
||||
uniform vec2 uClippingTexDim;
|
||||
uniform sampler2D tClipping;
|
||||
#if __VERSION__ != 300
|
||||
varying float vClipping;
|
||||
#else
|
||||
flat out float vClipping;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uniform vec2 uMarkerTexDim;
|
||||
uniform sampler2D tMarker;
|
||||
@@ -14,5 +32,6 @@ uniform sampler2D tMarker;
|
||||
flat out float vMarker;
|
||||
#endif
|
||||
|
||||
varying vec3 vModelPosition;
|
||||
varying vec3 vViewPosition;
|
||||
`;
|
||||
@@ -28,7 +28,6 @@ uniform sampler2D tMarker;
|
||||
|
||||
uniform float uAlpha;
|
||||
uniform float uPickingAlphaThreshold;
|
||||
uniform int uPickable;
|
||||
|
||||
#if defined(dGridTexType_2d)
|
||||
precision highp sampler2D;
|
||||
@@ -117,8 +116,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 viewDir) {
|
||||
#if defined(dRenderVariant_pick)
|
||||
if (uAlpha < uPickingAlphaThreshold)
|
||||
discard; // ignore so the element below can be picked
|
||||
if (uPickable == 0)
|
||||
return vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
|
||||
@@ -35,20 +35,20 @@ varying float vInstance;
|
||||
const float C = 0.333;
|
||||
#endif
|
||||
|
||||
float cubicFilter( float x ){
|
||||
float cubicFilter(float x){
|
||||
float f = x;
|
||||
if( f < 0.0 ){
|
||||
if (f < 0.0) {
|
||||
f = -f;
|
||||
}
|
||||
if( f < 1.0 ){
|
||||
return ( ( 12.0 - 9.0 * B - 6.0 * C ) * ( f * f * f ) +
|
||||
( -18.0 + 12.0 * B + 6.0 *C ) * ( f * f ) +
|
||||
( 6.0 - 2.0 * B ) ) / 6.0;
|
||||
}else if( f >= 1.0 && f < 2.0 ){
|
||||
return ( ( -B - 6.0 * C ) * ( f * f * f )
|
||||
+ ( 6.0 * B + 30.0 * C ) * ( f *f ) +
|
||||
( - ( 12.0 * B ) - 48.0 * C ) * f +
|
||||
8.0 * B + 24.0 * C ) / 6.0;
|
||||
if (f < 1.0) {
|
||||
return ((12.0 - 9.0 * B - 6.0 * C) * (f * f * f) +
|
||||
(-18.0 + 12.0 * B + 6.0 * C) * (f * f) +
|
||||
(6.0 - 2.0 * B)) / 6.0;
|
||||
}else if (f >= 1.0 && f < 2.0){
|
||||
return ((-B - 6.0 * C) * ( f * f * f)
|
||||
+ (6.0 * B + 30.0 * C) * (f * f) +
|
||||
(-(12.0 * B) - 48.0 * C) * f +
|
||||
8.0 * B + 24.0 * C) / 6.0;
|
||||
}else{
|
||||
return 0.0;
|
||||
}
|
||||
@@ -92,25 +92,23 @@ void main() {
|
||||
#else
|
||||
vec4 imageData = texture2D(tImageTex, vUv);
|
||||
#endif
|
||||
imageData.a = clamp(imageData.a, 0.0, 1.0);
|
||||
if (imageData.a > 0.9) imageData.a = 1.0;
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
if (imageData.a < 0.3)
|
||||
discard;
|
||||
|
||||
if (uPickable == 1) {
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
|
||||
#elif defined(dRenderVariant_pickInstance)
|
||||
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
|
||||
#elif defined(dRenderVariant_pickGroup)
|
||||
float group = texture2D(tGroupTex, vUv).r;
|
||||
gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
|
||||
#endif
|
||||
} else {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
|
||||
}
|
||||
#if defined(dRenderVariant_pickObject)
|
||||
gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
|
||||
#elif defined(dRenderVariant_pickInstance)
|
||||
gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
|
||||
#elif defined(dRenderVariant_pickGroup)
|
||||
float group = texture2D(tGroupTex, vUv).r;
|
||||
gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_depth)
|
||||
if (imageData.a < 0.01)
|
||||
if (imageData.a < 0.05)
|
||||
discard;
|
||||
|
||||
#ifdef enabledFragDepth
|
||||
@@ -119,7 +117,7 @@ void main() {
|
||||
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
|
||||
#endif
|
||||
#elif defined(dRenderVariant_color)
|
||||
if (imageData.a < 0.01)
|
||||
if (imageData.a < 0.05)
|
||||
discard;
|
||||
|
||||
gl_FragColor = imageData;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,8 +11,10 @@ precision highp int;
|
||||
#include common
|
||||
#include common_frag_params
|
||||
#include color_frag_params
|
||||
#include common_clip
|
||||
|
||||
void main(){
|
||||
#include clip_pixel
|
||||
#include assign_material_color
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*
|
||||
@@ -15,6 +15,7 @@ precision highp int;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include size_vert_params
|
||||
#include common_clip
|
||||
|
||||
uniform float uPixelRatio;
|
||||
uniform float uViewportHeight;
|
||||
@@ -42,6 +43,7 @@ void main(){
|
||||
#include assign_group
|
||||
#include assign_color_varying
|
||||
#include assign_marker_varying
|
||||
#include assign_clipping_varying
|
||||
#include assign_size
|
||||
|
||||
mat4 modelView = uView * uModel * aTransform;
|
||||
@@ -51,10 +53,12 @@ void main(){
|
||||
vec4 end = modelView * vec4(aEnd, 1.0);
|
||||
|
||||
// assign position
|
||||
vec3 position = (aMapping.y < 0.5) ? aStart : aEnd;
|
||||
vec4 mvPosition = modelView * vec4(position, 1.0);
|
||||
vec4 position4 = vec4((aMapping.y < 0.5) ? aStart : aEnd, 1.0);
|
||||
vec4 mvPosition = modelView * position4;
|
||||
vViewPosition = mvPosition.xyz;
|
||||
|
||||
vModelPosition = (uModel * aTransform * position4).xyz; // for clipping in frag shader
|
||||
|
||||
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
|
||||
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
|
||||
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
|
||||
@@ -114,5 +118,7 @@ void main(){
|
||||
offset *= clip.w;
|
||||
clip.xy += offset;
|
||||
gl_Position = clip;
|
||||
|
||||
#include clip_instance
|
||||
}
|
||||
`;
|
||||
@@ -13,8 +13,11 @@ precision highp int;
|
||||
#include color_frag_params
|
||||
#include light_frag_params
|
||||
#include normal_frag_params
|
||||
#include common_clip
|
||||
|
||||
void main() {
|
||||
#include clip_pixel
|
||||
|
||||
// Workaround for buggy gl_FrontFacing (e.g. on some integrated Intel GPUs)
|
||||
#if defined(enabledStandardDerivatives)
|
||||
vec3 fdx = dFdx(vViewPosition);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -12,6 +12,7 @@ precision highp int;
|
||||
#include read_from_texture
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include common_clip
|
||||
|
||||
#ifdef dGeoTexture
|
||||
uniform vec2 uGeoTexDim;
|
||||
@@ -34,7 +35,9 @@ void main(){
|
||||
#include assign_group
|
||||
#include assign_color_varying
|
||||
#include assign_marker_varying
|
||||
#include assign_clipping_varying
|
||||
#include assign_position
|
||||
#include clip_instance
|
||||
|
||||
#ifdef dGeoTexture
|
||||
vec3 normal = readFromTexture(tNormal, aGroup, uGeoTexDim).xyz;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ precision highp int;
|
||||
#include common
|
||||
#include common_frag_params
|
||||
#include color_frag_params
|
||||
#include common_clip
|
||||
|
||||
#ifdef dPointFilledCircle
|
||||
uniform float uPointEdgeBleach;
|
||||
@@ -20,6 +21,7 @@ const vec2 center = vec2(0.5);
|
||||
const float radius = 0.5;
|
||||
|
||||
void main(){
|
||||
#include clip_pixel
|
||||
#include assign_material_color
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,6 +13,7 @@ precision highp int;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include size_vert_params
|
||||
#include common_clip
|
||||
|
||||
uniform float uPixelRatio;
|
||||
uniform float uViewportHeight;
|
||||
@@ -26,6 +27,7 @@ void main(){
|
||||
#include assign_group
|
||||
#include assign_color_varying
|
||||
#include assign_marker_varying
|
||||
#include assign_clipping_varying
|
||||
#include assign_position
|
||||
#include assign_size
|
||||
|
||||
@@ -36,5 +38,7 @@ void main(){
|
||||
#endif
|
||||
|
||||
gl_Position = uProjection * mvPosition;
|
||||
|
||||
#include clip_instance
|
||||
}
|
||||
`;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -12,6 +12,7 @@ precision highp int;
|
||||
#include common_frag_params
|
||||
#include color_frag_params
|
||||
#include light_frag_params
|
||||
#include common_clip
|
||||
|
||||
uniform mat4 uProjection;
|
||||
|
||||
@@ -73,6 +74,8 @@ bool Impostor(out vec3 cameraPos, out vec3 cameraNormal){
|
||||
}
|
||||
|
||||
void main(void){
|
||||
#include clip_pixel
|
||||
|
||||
bool flag = Impostor(cameraPos, cameraNormal);
|
||||
#ifndef dDoubleSided
|
||||
if (interior)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,6 +13,7 @@ precision highp int;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include size_vert_params
|
||||
#include common_clip
|
||||
|
||||
uniform mat4 uModelView;
|
||||
uniform mat4 uInvProjection;
|
||||
@@ -77,11 +78,13 @@ void main(void){
|
||||
#include assign_group
|
||||
#include assign_color_varying
|
||||
#include assign_marker_varying
|
||||
#include assign_clipping_varying
|
||||
#include assign_size
|
||||
|
||||
vRadius = size * matrixScale(uModelView);
|
||||
|
||||
vec4 mvPosition = uModelView * aTransform * vec4(aPosition, 1.0);
|
||||
vec4 position4 = vec4(aPosition, 1.0);
|
||||
vec4 mvPosition = uModelView * aTransform * position4;
|
||||
mvPosition.z -= vRadius; // avoid clipping, added again in fragment shader
|
||||
|
||||
gl_Position = uProjection * vec4(mvPosition.xyz, 1.0);
|
||||
@@ -91,5 +94,9 @@ void main(void){
|
||||
vec4 vPoint4 = uInvProjection * gl_Position;
|
||||
vPoint = vPoint4.xyz / vPoint4.w;
|
||||
vPointViewPosition = -mvPosition.xyz / mvPosition.w;
|
||||
|
||||
vModelPosition = (uModel * aTransform * position4).xyz; // for clipping in frag shader
|
||||
|
||||
#include clip_instance
|
||||
}
|
||||
`;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ precision highp int;
|
||||
#include common
|
||||
#include common_frag_params
|
||||
#include color_frag_params
|
||||
#include common_clip
|
||||
|
||||
uniform sampler2D tFont;
|
||||
|
||||
@@ -29,6 +30,7 @@ void main2(){
|
||||
}
|
||||
|
||||
void main(){
|
||||
#include clip_pixel
|
||||
#include assign_material_color
|
||||
|
||||
if (vTexCoord.x > 1.0) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,6 +13,7 @@ precision highp int;
|
||||
#include common_vert_params
|
||||
#include color_vert_params
|
||||
#include size_vert_params
|
||||
#include common_clip
|
||||
|
||||
uniform mat4 uModelView;
|
||||
|
||||
@@ -40,6 +41,7 @@ void main(void){
|
||||
#include assign_group
|
||||
#include assign_color_varying
|
||||
#include assign_marker_varying
|
||||
#include assign_clipping_varying
|
||||
#include assign_size
|
||||
|
||||
vTexCoord = aTexCoord;
|
||||
@@ -50,7 +52,10 @@ void main(void){
|
||||
float offsetY = uOffsetY * scale;
|
||||
float offsetZ = (uOffsetZ + aDepth * 0.95) * scale;
|
||||
|
||||
vec4 mvPosition = uModelView * aTransform * vec4(aPosition, 1.0);
|
||||
vec4 position4 = vec4(aPosition, 1.0);
|
||||
vec4 mvPosition = uModelView * aTransform * position4;
|
||||
|
||||
vModelPosition = (uModel * aTransform * position4).xyz; // for clipping in frag shader
|
||||
|
||||
// TODO
|
||||
// #ifdef FIXED_SIZE
|
||||
@@ -64,7 +69,7 @@ void main(void){
|
||||
vec4 mvCorner = vec4(mvPosition.xyz, 1.0);
|
||||
|
||||
if (vTexCoord.x == 10.0) { // indicates background plane
|
||||
// move a bit to the back, tkaing ditsnace to camera into account to avoid z-fighting
|
||||
// move a bit to the back, taking distance to camera into account to avoid z-fighting
|
||||
offsetZ -= 0.001 * distance(uCameraPosition, (uProjection * mvCorner).xyz);
|
||||
}
|
||||
|
||||
@@ -83,5 +88,7 @@ void main(void){
|
||||
gl_Position = uProjection * mvCorner;
|
||||
|
||||
vViewPosition = -mvCorner.xyz;
|
||||
|
||||
#include clip_instance
|
||||
}
|
||||
`;
|
||||
@@ -86,7 +86,8 @@ function checkActiveUniforms(gl: GLRenderingContext, program: WebGLProgram, sche
|
||||
// name assigned by `gl.shim.ts`, ignore for checks
|
||||
continue;
|
||||
}
|
||||
const spec = schema[name];
|
||||
const baseName = name.replace(/[[0-9]+\]$/, ''); // 'array' uniforms
|
||||
const spec = schema[baseName];
|
||||
if (spec === undefined) {
|
||||
throw new Error(`missing 'uniform' or 'texture' with name '${name}' in schema`);
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,45 +8,33 @@ import { Mat3, Mat4, Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { GLRenderingContext } from './compat';
|
||||
import { RenderableSchema } from '../../mol-gl/renderable/schema';
|
||||
import { ValueOf } from '../../mol-util/type-helpers';
|
||||
|
||||
export type UniformKindValue = {
|
||||
'f': number
|
||||
'i': number
|
||||
'v2': Vec2
|
||||
'v3': Vec3
|
||||
'v4': Vec4
|
||||
'm3': Mat3
|
||||
'm4': Mat4
|
||||
't': number
|
||||
'f': number; 'f[]': number[]
|
||||
'i': number; 'i[]': number[]
|
||||
'v2': Vec2; 'v2[]': number[]
|
||||
'v3': Vec3; 'v3[]': number[]
|
||||
'v4': Vec4; 'v4[]': number[]
|
||||
'm3': Mat3; 'm3[]': number[]
|
||||
'm4': Mat4; 'm4[]': number[]
|
||||
't': number; 't[]': number[]
|
||||
}
|
||||
export type UniformKind = keyof UniformKindValue
|
||||
export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4
|
||||
export type UniformType = ValueOf<UniformKindValue>
|
||||
|
||||
export type UniformValues = { [k: string]: ValueCell<UniformType> }
|
||||
export type UniformsList = [string, ValueCell<UniformType>][]
|
||||
|
||||
export function getUniformType(gl: GLRenderingContext, kind: UniformKind) {
|
||||
switch (kind) {
|
||||
case 'f': return gl.FLOAT;
|
||||
case 'i': return gl.INT;
|
||||
case 'v2': return gl.FLOAT_VEC2;
|
||||
case 'v3': return gl.FLOAT_VEC3;
|
||||
case 'v4': return gl.FLOAT_VEC4;
|
||||
case 'm3': return gl.FLOAT_MAT3;
|
||||
case 'm4': return gl.FLOAT_MAT4;
|
||||
default: console.error(`unknown uniform kind '${kind}'`);
|
||||
}
|
||||
}
|
||||
|
||||
export function setUniform(gl: GLRenderingContext, location: WebGLUniformLocation | null, kind: UniformKind, value: any) {
|
||||
switch (kind) {
|
||||
case 'f': gl.uniform1f(location, value); break;
|
||||
case 'i': case 't': gl.uniform1i(location, value); break;
|
||||
case 'v2': gl.uniform2fv(location, value); break;
|
||||
case 'v3': gl.uniform3fv(location, value); break;
|
||||
case 'v4': gl.uniform4fv(location, value); break;
|
||||
case 'm3': gl.uniformMatrix3fv(location, false, value); break;
|
||||
case 'm4': gl.uniformMatrix4fv(location, false, value); break;
|
||||
case 'f': case 'f[]': return gl.FLOAT;
|
||||
case 'i': case 'i[]': return gl.INT;
|
||||
case 'v2': case 'v2[]': return gl.FLOAT_VEC2;
|
||||
case 'v3': case 'v3[]': return gl.FLOAT_VEC3;
|
||||
case 'v4': case 'v4[]': return gl.FLOAT_VEC4;
|
||||
case 'm3': case 'm3[]': return gl.FLOAT_MAT3;
|
||||
case 'm4': case 'm4[]': return gl.FLOAT_MAT4;
|
||||
default: console.error(`unknown uniform kind '${kind}'`);
|
||||
}
|
||||
}
|
||||
@@ -55,24 +43,27 @@ export type UniformSetter = (gl: GLRenderingContext, location: number, value: an
|
||||
export type UniformSetters = { [k: string]: UniformSetter }
|
||||
|
||||
function uniform1f (gl: GLRenderingContext, location: number, value: any) { gl.uniform1f(location, value); }
|
||||
function uniform1fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform1fv(location, value); }
|
||||
function uniform1i (gl: GLRenderingContext, location: number, value: any) { gl.uniform1i(location, value); }
|
||||
function uniform1iv (gl: GLRenderingContext, location: number, value: any) { gl.uniform1iv(location, value); }
|
||||
function uniform2fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform2fv(location, value); }
|
||||
function uniform3fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform3fv(location, value); }
|
||||
function uniform4fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform4fv(location, value); }
|
||||
function uniformMatrix3fv (gl: GLRenderingContext, location: number, value: any) { gl.uniformMatrix3fv(location, false, value); }
|
||||
function uniformMatrix4fv (gl: GLRenderingContext, location: number, value: any) { gl.uniformMatrix4fv(location, false, value); }
|
||||
|
||||
function getUniformSetter(kind: UniformKind) {
|
||||
function getUniformSetter(kind: UniformKind): UniformSetter {
|
||||
switch (kind) {
|
||||
case 'f': return uniform1f;
|
||||
case 'f[]': return uniform1fv;
|
||||
case 'i': case 't': return uniform1i;
|
||||
case 'v2': return uniform2fv;
|
||||
case 'v3': return uniform3fv;
|
||||
case 'v4': return uniform4fv;
|
||||
case 'm3': return uniformMatrix3fv;
|
||||
case 'm4': return uniformMatrix4fv;
|
||||
case 'i[]': case 't[]': return uniform1iv;
|
||||
case 'v2': case 'v2[]': return uniform2fv;
|
||||
case 'v3': case 'v3[]': return uniform3fv;
|
||||
case 'v4': case 'v4[]': return uniform4fv;
|
||||
case 'm3': case 'm3[]': return uniformMatrix3fv;
|
||||
case 'm4': case 'm4[]': return uniformMatrix4fv;
|
||||
}
|
||||
throw new Error(`unknown uniform kind '${kind}'`);
|
||||
}
|
||||
|
||||
export function getUniformSetters(schema: RenderableSchema) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SimpleBuffer } from './simple-buffer';
|
||||
const fs = typeof document === 'undefined' ? require('fs') as typeof import('fs') : void 0;
|
||||
|
||||
export interface FileHandle {
|
||||
name: string
|
||||
/**
|
||||
* Asynchronously reads data, returning buffer and number of bytes read
|
||||
*
|
||||
@@ -44,8 +45,9 @@ export interface FileHandle {
|
||||
}
|
||||
|
||||
export namespace FileHandle {
|
||||
export function fromBuffer(buffer: SimpleBuffer): FileHandle {
|
||||
export function fromBuffer(buffer: SimpleBuffer, name: string): FileHandle {
|
||||
return {
|
||||
name,
|
||||
readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, size?: number, byteOffset?: number) => {
|
||||
let bytesRead: number;
|
||||
let outBuffer: SimpleBuffer;
|
||||
@@ -82,9 +84,10 @@ export namespace FileHandle {
|
||||
};
|
||||
}
|
||||
|
||||
export function fromDescriptor(file: number): FileHandle {
|
||||
export function fromDescriptor(file: number, name: string): FileHandle {
|
||||
if (fs === undefined) throw new Error('fs module not available');
|
||||
return {
|
||||
name,
|
||||
readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number) => {
|
||||
return new Promise((res, rej) => {
|
||||
let outBuffer: SimpleBuffer;
|
||||
|
||||
@@ -31,7 +31,7 @@ function createCcp4Data() {
|
||||
describe('ccp4 reader', () => {
|
||||
it('basic', async () => {
|
||||
const data = createCcp4Data();
|
||||
const parsed = await CCP4.parse(data).run();
|
||||
const parsed = await CCP4.parse(data, 'test.ccp4').run();
|
||||
|
||||
if (parsed.isError) {
|
||||
throw new Error(parsed.message);
|
||||
|
||||
@@ -8,10 +8,14 @@ import { parseFloat as fastParseFloat, parseInt as fastParseInt, getNumberType,
|
||||
|
||||
describe('common', () => {
|
||||
it('number-parser fastParseFloat', () => {
|
||||
// ignore suffix numbers in parentheses
|
||||
expect(fastParseFloat('11.0829(23)', 0, 11)).toBe(11.0829);
|
||||
// scientific with no space between consecutive values
|
||||
expect(fastParseFloat('-5.1E-01-6.1E-01', 0, 11)).toBe(-0.51);
|
||||
});
|
||||
|
||||
it('number-parser fastParseInt', () => {
|
||||
// ignore suffix numbers in parentheses
|
||||
expect(fastParseInt('11(23)', 0, 11)).toBe(11);
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user