mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 07:04:22 +08:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c5f451878 | ||
|
|
6a5fb85c5a | ||
|
|
2aef5fb3e5 | ||
|
|
63a9aef5eb | ||
|
|
e36fe8c707 | ||
|
|
0585f7b9ca | ||
|
|
25ab0d7799 | ||
|
|
ea313a442c | ||
|
|
57a63e0381 | ||
|
|
c525812aee | ||
|
|
e602705e6d | ||
|
|
b7d126b39b | ||
|
|
80b2864da8 | ||
|
|
36ba6f035a | ||
|
|
b84bbdace2 | ||
|
|
750b2f69fc | ||
|
|
6ffe051a8e | ||
|
|
bc3640b264 | ||
|
|
8746b6e2f4 | ||
|
|
445977d99b | ||
|
|
0f0185e18c | ||
|
|
a748b1581e | ||
|
|
af1e06203b | ||
|
|
356cf008ce | ||
|
|
07284e7e3d | ||
|
|
72055442b7 | ||
|
|
5fa7d84c23 | ||
|
|
de46f08bf4 | ||
|
|
fde395e2fa | ||
|
|
da1e55250e | ||
|
|
a4ab117d14 | ||
|
|
4cd7088eb8 | ||
|
|
8e1876fc25 | ||
|
|
29693ebe8c | ||
|
|
a8ee7bfcbe | ||
|
|
2037dad03d | ||
|
|
0a9fb603f6 | ||
|
|
0b7dadd345 | ||
|
|
a3645f5acc | ||
|
|
92f409d6fc | ||
|
|
c28dd8135c | ||
|
|
3baa03ccdc | ||
|
|
659e96d93c | ||
|
|
365d7d46fd | ||
|
|
f8284508e1 | ||
|
|
e9c36c2375 | ||
|
|
5d269fd77c | ||
|
|
0064293e01 | ||
|
|
ba4b6f70d3 | ||
|
|
9d056a85ec | ||
|
|
3ed17fce6b | ||
|
|
d656dd0d18 | ||
|
|
27c92085c7 | ||
|
|
94e9462af8 | ||
|
|
2e52ccf5d8 | ||
|
|
b15196a284 | ||
|
|
d29cc85439 | ||
|
|
30367cf239 | ||
|
|
dcf9d1c3bb | ||
|
|
c0c2e4ce4a | ||
|
|
3557f4e738 | ||
|
|
6595c00cff | ||
|
|
c612018ba8 | ||
|
|
ec06ef2dfb | ||
|
|
2febf45700 | ||
|
|
3e6066a1a1 | ||
|
|
b61e5c76db | ||
|
|
bdee4859f2 | ||
|
|
b5e45aae95 | ||
|
|
7fd12c5622 | ||
|
|
47442be989 | ||
|
|
3a484dc41a | ||
|
|
9a2dfd7e57 | ||
|
|
c9a168a138 | ||
|
|
3726f28eeb | ||
|
|
ead25d6a9c | ||
|
|
55f4abb6be | ||
|
|
2e013fafc8 | ||
|
|
0809379f91 | ||
|
|
0d4a95f5af | ||
|
|
2e9129a71c | ||
|
|
7384bebf4e | ||
|
|
0e197b1885 | ||
|
|
54697ae0d5 | ||
|
|
7b6eb9337e | ||
|
|
a94fb84052 | ||
|
|
528aeb1873 | ||
|
|
e7b35daf45 | ||
|
|
7d10971617 | ||
|
|
3d83211503 | ||
|
|
cd8c8fa020 | ||
|
|
4c03009357 | ||
|
|
047f547863 | ||
|
|
83c62c614b | ||
|
|
c39b3569de | ||
|
|
7fea62aa46 | ||
|
|
d816f510ea |
@@ -18,7 +18,7 @@
|
||||
],
|
||||
"brace-style": "off",
|
||||
"comma-spacing": "off",
|
||||
"space-infix-ops": "error",
|
||||
"space-infix-ops": "off",
|
||||
"comma-dangle": "off",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
@@ -107,6 +107,7 @@
|
||||
"1tbs", { "allowSingleLine": true }
|
||||
],
|
||||
"@typescript-eslint/comma-spacing": "error",
|
||||
"@typescript-eslint/space-infix-ops": "error",
|
||||
"@typescript-eslint/space-before-function-paren": ["error", {
|
||||
"anonymous": "always",
|
||||
"named": "never",
|
||||
|
||||
4
.github/workflows/node.yml
vendored
4
.github/workflows/node.yml
vendored
@@ -1,3 +1,5 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
@@ -15,6 +17,6 @@ jobs:
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
- name: Test
|
||||
run: npm install --no-save "gl@^5.0.0" && xvfb-run --auto-servernum npm run jest
|
||||
run: npm install --no-save "gl@^6.0.2" && xvfb-run --auto-servernum npm run jest
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
18
.travis.yml
18
.travis.yml
@@ -1,18 +0,0 @@
|
||||
language: node_js
|
||||
os: linux
|
||||
sudo: required
|
||||
dist: trusty
|
||||
before_install:
|
||||
- sudo apt-get install -y mesa-utils
|
||||
- sudo apt-get install -y xvfb
|
||||
- sudo apt-get install -y libgl1-mesa-dri
|
||||
- sudo apt-get install -y libglapi-mesa
|
||||
- sudo apt-get install -y libosmesa6
|
||||
- sudo apt-get install -y gcc-4.9
|
||||
- sudo apt-get install -y libstdc++6
|
||||
- sudo apt-get install -y libxi-dev
|
||||
node_js:
|
||||
- "12"
|
||||
- "10"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start
|
||||
59
CHANGELOG.md
59
CHANGELOG.md
@@ -6,12 +6,71 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v3.31.2] - 2023-02-12
|
||||
|
||||
- Fix exit code of volume pack executable (pack.ts). Now exits with non-0 status when an error happens
|
||||
- Remove pca transform from components ui focus (too distracting)
|
||||
- Fix artefacts with opaque outlines behind transparent objects
|
||||
- Fix polymer trace visual not updating
|
||||
- Fix use of `WEBGL_provoking_vertex`
|
||||
|
||||
## [v3.31.1] - 2023-02-05
|
||||
|
||||
- Improve Component camera focus based on the PCA of the structure and the following rules:
|
||||
- The first residue should be in first quadrant if there is only one chain
|
||||
- The average position of the residues of the first chain should be in the first quadrant if there is more than one chain
|
||||
- Add `HeadlessPluginContext` and `HeadlessScreenshotHelper` to be used in Node.js
|
||||
- Add example `image-renderer`
|
||||
- Fix wrong offset when rendering text with orthographic projection
|
||||
- Update camera/handle helper when `devicePixelRatio` changes
|
||||
- Add various options to customize the axes camera-helper
|
||||
- Fix issue with texture-mesh color smoothing when changing themes
|
||||
- Add fast boundary helper and corresponding unit trait
|
||||
- Add Observable for Canvas3D commits
|
||||
|
||||
## [v3.30.0] - 2023-01-29
|
||||
|
||||
- Improve `Dnatco` extension
|
||||
- Factor out common code in `Dnatco` extension
|
||||
- Add `NtC tube` visual. Applicable for structures with NtC annotation
|
||||
- [Breaking] Rename `DnatcoConfalPyramids` to `DnatcoNtCs`
|
||||
- Improve boundary calculation performance
|
||||
- Add option to create & include images in state snapshots
|
||||
- Fix SSAO artefacts with high bias values
|
||||
- Fix SSAO resolution scale parameter handling
|
||||
- Improve outlines, visually more stable at different view distances
|
||||
|
||||
## [v3.29.0] - 2023-01-15
|
||||
|
||||
- `meshes` extension: Fixed a bug in mesh visualization (show backfaces when opacity < 1)
|
||||
- Add color quick select control to Volume controls
|
||||
- Fix `dropFiles` bug
|
||||
- Fix some cyclic imports and reduce the use of const enums. This should make it easier to use the library with the `isolatedModules: true` TS config.
|
||||
- Fix `dropFiles` bug (#679)
|
||||
- Add `input type='color'` picker to `CombinedColorControl`
|
||||
- Set `ParameterMappingControl` disabled when state is updating
|
||||
- Performance tweaks
|
||||
- Update clip `defines` only when changed
|
||||
- Check for identity in structure/unit areEqual methods
|
||||
- Avoid cloning of structure representation parameters
|
||||
- Make SymmetryOperator.createMapping monomorphic
|
||||
- Improve bonding-sphere calculation
|
||||
- Defer Scene properties calculation (markerAverage, opacityAverage, hasOpaque)
|
||||
- Improve checks in in UnitsRepresentation setVisualState
|
||||
- Add StructureElement.Loci.forEachLocation
|
||||
- Add RepresentationRegistry.clear and ThemeRegistry.clear
|
||||
- Add generic Loci support for overpaint, substance, clipping themes
|
||||
- Add `.getCenter` and `.center` to `Camera`
|
||||
- Add support to dim unmarked groups
|
||||
- Add support for marker edge strength
|
||||
|
||||
## [v3.28.0] - 2022-12-20
|
||||
|
||||
- Show histogram in direct volume control point settings
|
||||
- Add `solidInterior` parameter to sphere/cylinder impostors
|
||||
- [Breaking] Tweak `ignoreHydrogens` non-polar handling (introduced in 3.27.0)
|
||||
- Add `meshes` and `volumes-and-segmentations` extensions
|
||||
- See https://molstarvolseg.ncbr.muni.cz/ for more info
|
||||
- Fix missing support for info in `ParamDefinition.Converted`
|
||||
- Add support for multi-visual volume representations
|
||||
- Improve volume isosurface bounding-sphere
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[](./LICENSE)
|
||||
[](https://www.npmjs.com/package/molstar)
|
||||
[](https://travis-ci.org/molstar/molstar)
|
||||
[](https://github.com/molstar/molstar/actions/workflows/node.yml)
|
||||
[](https://gitter.im/molstar/Lobby)
|
||||
|
||||
# Mol*
|
||||
|
||||
6194
package-lock.json
generated
6194
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
56
package.json
56
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "3.28.0",
|
||||
"version": "3.31.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix",
|
||||
"test": "npm install --no-save \"gl@^5.0.0\" && npm run lint && jest",
|
||||
"test": "npm install --no-save \"gl@^6.0.2\" && npm run lint && jest",
|
||||
"jest": "jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"clean": "node ./scripts/clean.js",
|
||||
@@ -93,49 +93,52 @@
|
||||
"Adam Midlik <midlik@gmail.com>",
|
||||
"Koya Sakuma <koya.sakuma.work@gmail.com>",
|
||||
"Gianluca Tomasello <giagitom@gmail.com>",
|
||||
"Ke Ma <mark.ma@rcsb.org>",
|
||||
"Jason Pattle <jpattle@exscientia.co.uk>",
|
||||
"David Williams <dwilliams@nobiastx.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^3.2.3",
|
||||
"@graphql-codegen/cli": "^2.16.1",
|
||||
"@graphql-codegen/time": "^3.2.3",
|
||||
"@graphql-codegen/typescript": "^2.8.5",
|
||||
"@graphql-codegen/add": "^4.0.0",
|
||||
"@graphql-codegen/cli": "^3.0.0",
|
||||
"@graphql-codegen/time": "^4.0.0",
|
||||
"@graphql-codegen/typescript": "^3.0.0",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.8",
|
||||
"@graphql-codegen/typescript-operations": "^2.5.10",
|
||||
"@graphql-codegen/typescript-operations": "^3.0.0",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/gl": "^6.0.2",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"@types/jpeg-js": "^0.3.7",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^7.6.0",
|
||||
"cpx2": "^4.2.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint": "^8.33.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"graphql": "^16.6.0",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^29.3.1",
|
||||
"jest": "^29.4.1",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.57.0",
|
||||
"sass": "^1.58.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"simple-git": "^3.15.1",
|
||||
"simple-git": "^3.16.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.9.4",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
},
|
||||
@@ -143,8 +146,8 @@
|
||||
"@types/argparse": "^2.0.10",
|
||||
"@types/benchmark": "^2.1.2",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.15",
|
||||
"@types/node": "^16.18.10",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/node": "^16.18.12",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
@@ -153,17 +156,22 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^9.0.16",
|
||||
"immutable": "^4.1.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"immer": "^9.0.19",
|
||||
"immutable": "^4.2.3",
|
||||
"node-fetch": "^2.6.9",
|
||||
"rxjs": "^7.8.0",
|
||||
"swagger-ui-dist": "^4.15.5",
|
||||
"tslib": "^2.4.1",
|
||||
"tslib": "^2.5.0",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0 || ^17.0.2 || ^16.14.0",
|
||||
"react-dom": "^18.1.0 || ^17.0.2 || ^16.14.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"gl": "^6.0.2",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"pngjs": "^6.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
|
||||
import { CellPack } from '../../extensions/cellpack';
|
||||
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
|
||||
import { DnatcoNtCs } from '../../extensions/dnatco';
|
||||
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
|
||||
import { Volseg, VolsegVolumeServerConfig } from '../../extensions/volumes-and-segmentations';
|
||||
import { GeometryExport } from '../../extensions/geo-export';
|
||||
@@ -50,7 +50,7 @@ import { SaccharideCompIdMapType } from '../../mol-model/structure/structure/car
|
||||
import { Backgrounds } from '../../extensions/backgrounds';
|
||||
|
||||
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
|
||||
export { setDebugMode, setProductionMode, setTimingMode } from '../../mol-util/debug';
|
||||
export { setDebugMode, setProductionMode, setTimingMode, consoleStats } from '../../mol-util/debug';
|
||||
|
||||
const CustomFormats = [
|
||||
['g3d', G3dProvider] as const
|
||||
@@ -60,7 +60,7 @@ const Extensions = {
|
||||
'volseg': PluginSpec.Behavior(Volseg),
|
||||
'backgrounds': PluginSpec.Behavior(Backgrounds),
|
||||
'cellpack': PluginSpec.Behavior(CellPack),
|
||||
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
|
||||
'dnatco-ntcs': PluginSpec.Behavior(DnatcoNtCs),
|
||||
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
|
||||
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
|
||||
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
|
||||
|
||||
@@ -152,7 +152,7 @@ function getImportFrames(d: Data.CifFrame, imports: Imports) {
|
||||
}
|
||||
|
||||
/** get field from given or linked category */
|
||||
function getField(category: string, field: string, d: Data.CifFrame, imports: Imports, ctx: FrameData): Data.CifField|undefined {
|
||||
function getField(category: string, field: string, d: Data.CifFrame, imports: Imports, ctx: FrameData): Data.CifField | undefined {
|
||||
const { categories, links } = ctx;
|
||||
const cat = d.categories[category];
|
||||
if (cat) {
|
||||
|
||||
@@ -25,7 +25,7 @@ async function readFile(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function parseCif(data: string|Uint8Array) {
|
||||
async function parseCif(data: string | Uint8Array) {
|
||||
const comp = CIF.parse(data);
|
||||
const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
|
||||
if (parsed.isError) throw parsed;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { DemoMoleculeSDF, DemoOrbitals } from './example-data';
|
||||
import './index.html';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
import { setDebugMode, setTimingMode } from '../../mol-util/debug';
|
||||
import { setDebugMode, setTimingMode, consoleStats } from '../../mol-util/debug';
|
||||
|
||||
interface DemoInput {
|
||||
moleculeSdf: string,
|
||||
@@ -222,4 +222,5 @@ export class AlphaOrbitalsExample {
|
||||
|
||||
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
|
||||
(window as any).AlphaOrbitalsExample.setDebugMode = setDebugMode;
|
||||
(window as any).AlphaOrbitalsExample.setTimingMode = setTimingMode;
|
||||
(window as any).AlphaOrbitalsExample.setTimingMode = setTimingMode;
|
||||
(window as any).AlphaOrbitalsExample.consoleStats = consoleStats;
|
||||
87
src/examples/image-renderer/index.ts
Normal file
87
src/examples/image-renderer/index.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*
|
||||
* Example command-line application generating images of PDB structures
|
||||
* Build: npm install --no-save gl jpeg-js pngjs // these packages are not listed in dependencies for performance reasons
|
||||
* npm run build
|
||||
* Run: node lib/commonjs/examples/image-renderer 1cbs ../outputs_1cbs/
|
||||
*/
|
||||
|
||||
import { ArgumentParser } from 'argparse';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { Download, ParseCif } from '../../mol-plugin-state/transforms/data';
|
||||
import { ModelFromTrajectory, StructureComponent, StructureFromModel, TrajectoryFromMmCif } from '../../mol-plugin-state/transforms/model';
|
||||
import { StructureRepresentation3D } from '../../mol-plugin-state/transforms/representation';
|
||||
import { HeadlessPluginContext } from '../../mol-plugin/headless-plugin-context';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import { STYLIZED_POSTPROCESSING } from '../../mol-plugin/util/headless-screenshot';
|
||||
|
||||
|
||||
interface Args {
|
||||
pdbId: string,
|
||||
outDirectory: string
|
||||
}
|
||||
|
||||
function parseArguments(): Args {
|
||||
const parser = new ArgumentParser({ description: 'Example command-line application generating images of PDB structures' });
|
||||
parser.add_argument('pdbId', { help: 'PDB identifier' });
|
||||
parser.add_argument('outDirectory', { help: 'Directory for outputs' });
|
||||
const args = parser.parse_args();
|
||||
return { ...args };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArguments();
|
||||
const url = `https://www.ebi.ac.uk/pdbe/entry-files/download/${args.pdbId}.bcif`;
|
||||
console.log('PDB ID:', args.pdbId);
|
||||
console.log('Source URL:', url);
|
||||
console.log('Outputs:', args.outDirectory);
|
||||
|
||||
// Create a headless plugin
|
||||
const plugin = new HeadlessPluginContext(DefaultPluginSpec(), { width: 800, height: 800 });
|
||||
await plugin.init();
|
||||
|
||||
// Download and visualize data in the plugin
|
||||
const update = plugin.build();
|
||||
const structure = update.toRoot()
|
||||
.apply(Download, { url, isBinary: true })
|
||||
.apply(ParseCif)
|
||||
.apply(TrajectoryFromMmCif)
|
||||
.apply(ModelFromTrajectory)
|
||||
.apply(StructureFromModel);
|
||||
const polymer = structure.apply(StructureComponent, { type: { name: 'static', params: 'polymer' } });
|
||||
const ligand = structure.apply(StructureComponent, { type: { name: 'static', params: 'ligand' } });
|
||||
polymer.apply(StructureRepresentation3D, {
|
||||
type: { name: 'cartoon', params: { alpha: 1 } },
|
||||
colorTheme: { name: 'sequence-id', params: {} },
|
||||
});
|
||||
ligand.apply(StructureRepresentation3D, {
|
||||
type: { name: 'ball-and-stick', params: { sizeFactor: 1 } },
|
||||
colorTheme: { name: 'element-symbol', params: { carbonColor: { name: 'element-symbol', params: {} } } },
|
||||
sizeTheme: { name: 'physical', params: {} },
|
||||
});
|
||||
await update.commit();
|
||||
|
||||
// Export images
|
||||
fs.mkdirSync(args.outDirectory, { recursive: true });
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'basic.png'));
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'basic.jpg'));
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'large.png'), { width: 1600, height: 1200 });
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'large.jpg'), { width: 1600, height: 1200 });
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'stylized.png'), undefined, STYLIZED_POSTPROCESSING);
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'stylized.jpg'), undefined, STYLIZED_POSTPROCESSING);
|
||||
await plugin.saveImage(path.join(args.outDirectory, 'stylized-compressed-jpg.jpg'), undefined, STYLIZED_POSTPROCESSING, undefined, 10);
|
||||
|
||||
// Export state loadable in Mol* Viewer
|
||||
await plugin.saveStateSnapshot(path.join(args.outDirectory, 'molstar-state.molj'));
|
||||
|
||||
// Cleanup
|
||||
await plugin.clear();
|
||||
plugin.dispose();
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -52,7 +52,7 @@ export interface Compartment {
|
||||
}
|
||||
|
||||
// Primitives discribing a compartment
|
||||
export const enum CompartmentPrimitiveType {
|
||||
export enum CompartmentPrimitiveType {
|
||||
MetaBall = 0,
|
||||
Sphere = 1,
|
||||
Cube = 2,
|
||||
|
||||
@@ -236,7 +236,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
structure = await getCurve(name, getCurveTransforms(ingredient), model);
|
||||
} else {
|
||||
if ((!results || results.length === 0)) return;
|
||||
let bu: string|undefined = source.bu ? source.bu : undefined;
|
||||
let bu: string | undefined = source.bu ? source.bu : undefined;
|
||||
if (bu) {
|
||||
if (bu === 'AU') {
|
||||
bu = undefined;
|
||||
|
||||
59
src/extensions/dnatco/behavior.ts
Normal file
59
src/extensions/dnatco/behavior.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ConfalPyramidsPreset } from './confal-pyramids/behavior';
|
||||
import { ConfalPyramidsColorThemeProvider } from './confal-pyramids/color';
|
||||
import { ConfalPyramidsProvider } from './confal-pyramids/property';
|
||||
import { ConfalPyramidsRepresentationProvider } from './confal-pyramids/representation';
|
||||
import { NtCTubePreset } from './ntc-tube/behavior';
|
||||
import { NtCTubeColorThemeProvider } from './ntc-tube/color';
|
||||
import { NtCTubeProvider } from './ntc-tube/property';
|
||||
import { NtCTubeRepresentationProvider } from './ntc-tube/representation';
|
||||
|
||||
|
||||
export const DnatcoNtCs = PluginBehavior.create<{ autoAttach: boolean, showToolTip: boolean }>({
|
||||
name: 'dnatco-ntcs',
|
||||
category: 'custom-props',
|
||||
display: {
|
||||
name: 'DNATCO NtC Annotations',
|
||||
description: 'DNATCO NtC Annotations',
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
|
||||
register(): void {
|
||||
this.ctx.customModelProperties.register(ConfalPyramidsProvider, this.params.autoAttach);
|
||||
this.ctx.customModelProperties.register(NtCTubeProvider, this.params.autoAttach);
|
||||
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(NtCTubeColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.add(NtCTubeRepresentationProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.registerPreset(ConfalPyramidsPreset);
|
||||
this.ctx.builders.structure.representation.registerPreset(NtCTubePreset);
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
|
||||
this.ctx.customModelProperties.unregister(NtCTubeProvider.descriptor.name);
|
||||
|
||||
this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.remove(NtCTubeRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(NtCTubeColorThemeProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.unregisterPreset(ConfalPyramidsPreset);
|
||||
this.ctx.builders.structure.representation.unregisterPreset(NtCTubePreset);
|
||||
}
|
||||
},
|
||||
params: () => ({
|
||||
autoAttach: PD.Boolean(true),
|
||||
showToolTip: PD.Boolean(true)
|
||||
})
|
||||
});
|
||||
|
||||
219
src/extensions/dnatco/color.ts
Normal file
219
src/extensions/dnatco/color.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { Color, ColorMap } from '../../mol-util/color';
|
||||
|
||||
export const DefaultNtCClassColors = {
|
||||
A: 0xFFC1C1,
|
||||
B: 0xC8CFFF,
|
||||
BII: 0x0059DA,
|
||||
miB: 0x3BE8FB,
|
||||
Z: 0x01F60E,
|
||||
IC: 0xFA5CFB,
|
||||
OPN: 0xE90000,
|
||||
SYN: 0xFFFF01,
|
||||
N: 0xF2F2F2,
|
||||
};
|
||||
export const ErrorColor = Color(0xFFA10A);
|
||||
|
||||
export const NtCColors = ColorMap({
|
||||
NANT_Upr: DefaultNtCClassColors.N,
|
||||
NANT_Lwr: DefaultNtCClassColors.N,
|
||||
AA00_Upr: DefaultNtCClassColors.A,
|
||||
AA00_Lwr: DefaultNtCClassColors.A,
|
||||
AA02_Upr: DefaultNtCClassColors.A,
|
||||
AA02_Lwr: DefaultNtCClassColors.A,
|
||||
AA03_Upr: DefaultNtCClassColors.A,
|
||||
AA03_Lwr: DefaultNtCClassColors.A,
|
||||
AA04_Upr: DefaultNtCClassColors.A,
|
||||
AA04_Lwr: DefaultNtCClassColors.A,
|
||||
AA08_Upr: DefaultNtCClassColors.A,
|
||||
AA08_Lwr: DefaultNtCClassColors.A,
|
||||
AA09_Upr: DefaultNtCClassColors.A,
|
||||
AA09_Lwr: DefaultNtCClassColors.A,
|
||||
AA01_Upr: DefaultNtCClassColors.A,
|
||||
AA01_Lwr: DefaultNtCClassColors.A,
|
||||
AA05_Upr: DefaultNtCClassColors.A,
|
||||
AA05_Lwr: DefaultNtCClassColors.A,
|
||||
AA06_Upr: DefaultNtCClassColors.A,
|
||||
AA06_Lwr: DefaultNtCClassColors.A,
|
||||
AA10_Upr: DefaultNtCClassColors.A,
|
||||
AA10_Lwr: DefaultNtCClassColors.A,
|
||||
AA11_Upr: DefaultNtCClassColors.A,
|
||||
AA11_Lwr: DefaultNtCClassColors.A,
|
||||
AA07_Upr: DefaultNtCClassColors.A,
|
||||
AA07_Lwr: DefaultNtCClassColors.A,
|
||||
AA12_Upr: DefaultNtCClassColors.A,
|
||||
AA12_Lwr: DefaultNtCClassColors.A,
|
||||
AA13_Upr: DefaultNtCClassColors.A,
|
||||
AA13_Lwr: DefaultNtCClassColors.A,
|
||||
AB01_Upr: DefaultNtCClassColors.A,
|
||||
AB01_Lwr: DefaultNtCClassColors.B,
|
||||
AB02_Upr: DefaultNtCClassColors.A,
|
||||
AB02_Lwr: DefaultNtCClassColors.B,
|
||||
AB03_Upr: DefaultNtCClassColors.A,
|
||||
AB03_Lwr: DefaultNtCClassColors.B,
|
||||
AB04_Upr: DefaultNtCClassColors.A,
|
||||
AB04_Lwr: DefaultNtCClassColors.B,
|
||||
AB05_Upr: DefaultNtCClassColors.A,
|
||||
AB05_Lwr: DefaultNtCClassColors.B,
|
||||
BA01_Upr: DefaultNtCClassColors.B,
|
||||
BA01_Lwr: DefaultNtCClassColors.A,
|
||||
BA05_Upr: DefaultNtCClassColors.B,
|
||||
BA05_Lwr: DefaultNtCClassColors.A,
|
||||
BA09_Upr: DefaultNtCClassColors.B,
|
||||
BA09_Lwr: DefaultNtCClassColors.A,
|
||||
BA08_Upr: DefaultNtCClassColors.BII,
|
||||
BA08_Lwr: DefaultNtCClassColors.A,
|
||||
BA10_Upr: DefaultNtCClassColors.B,
|
||||
BA10_Lwr: DefaultNtCClassColors.A,
|
||||
BA13_Upr: DefaultNtCClassColors.BII,
|
||||
BA13_Lwr: DefaultNtCClassColors.A,
|
||||
BA16_Upr: DefaultNtCClassColors.BII,
|
||||
BA16_Lwr: DefaultNtCClassColors.A,
|
||||
BA17_Upr: DefaultNtCClassColors.BII,
|
||||
BA17_Lwr: DefaultNtCClassColors.A,
|
||||
BB00_Upr: DefaultNtCClassColors.B,
|
||||
BB00_Lwr: DefaultNtCClassColors.B,
|
||||
BB01_Upr: DefaultNtCClassColors.B,
|
||||
BB01_Lwr: DefaultNtCClassColors.B,
|
||||
BB17_Upr: DefaultNtCClassColors.B,
|
||||
BB17_Lwr: DefaultNtCClassColors.B,
|
||||
BB02_Upr: DefaultNtCClassColors.B,
|
||||
BB02_Lwr: DefaultNtCClassColors.B,
|
||||
BB03_Upr: DefaultNtCClassColors.B,
|
||||
BB03_Lwr: DefaultNtCClassColors.B,
|
||||
BB11_Upr: DefaultNtCClassColors.B,
|
||||
BB11_Lwr: DefaultNtCClassColors.B,
|
||||
BB16_Upr: DefaultNtCClassColors.B,
|
||||
BB16_Lwr: DefaultNtCClassColors.B,
|
||||
BB04_Upr: DefaultNtCClassColors.B,
|
||||
BB04_Lwr: DefaultNtCClassColors.BII,
|
||||
BB05_Upr: DefaultNtCClassColors.B,
|
||||
BB05_Lwr: DefaultNtCClassColors.BII,
|
||||
BB07_Upr: DefaultNtCClassColors.BII,
|
||||
BB07_Lwr: DefaultNtCClassColors.BII,
|
||||
BB08_Upr: DefaultNtCClassColors.BII,
|
||||
BB08_Lwr: DefaultNtCClassColors.BII,
|
||||
BB10_Upr: DefaultNtCClassColors.miB,
|
||||
BB10_Lwr: DefaultNtCClassColors.miB,
|
||||
BB12_Upr: DefaultNtCClassColors.miB,
|
||||
BB12_Lwr: DefaultNtCClassColors.miB,
|
||||
BB13_Upr: DefaultNtCClassColors.miB,
|
||||
BB13_Lwr: DefaultNtCClassColors.miB,
|
||||
BB14_Upr: DefaultNtCClassColors.miB,
|
||||
BB14_Lwr: DefaultNtCClassColors.miB,
|
||||
BB15_Upr: DefaultNtCClassColors.miB,
|
||||
BB15_Lwr: DefaultNtCClassColors.miB,
|
||||
BB20_Upr: DefaultNtCClassColors.miB,
|
||||
BB20_Lwr: DefaultNtCClassColors.miB,
|
||||
IC01_Upr: DefaultNtCClassColors.IC,
|
||||
IC01_Lwr: DefaultNtCClassColors.IC,
|
||||
IC02_Upr: DefaultNtCClassColors.IC,
|
||||
IC02_Lwr: DefaultNtCClassColors.IC,
|
||||
IC03_Upr: DefaultNtCClassColors.IC,
|
||||
IC03_Lwr: DefaultNtCClassColors.IC,
|
||||
IC04_Upr: DefaultNtCClassColors.IC,
|
||||
IC04_Lwr: DefaultNtCClassColors.IC,
|
||||
IC05_Upr: DefaultNtCClassColors.IC,
|
||||
IC05_Lwr: DefaultNtCClassColors.IC,
|
||||
IC06_Upr: DefaultNtCClassColors.IC,
|
||||
IC06_Lwr: DefaultNtCClassColors.IC,
|
||||
IC07_Upr: DefaultNtCClassColors.IC,
|
||||
IC07_Lwr: DefaultNtCClassColors.IC,
|
||||
OP01_Upr: DefaultNtCClassColors.OPN,
|
||||
OP01_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP02_Upr: DefaultNtCClassColors.OPN,
|
||||
OP02_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP03_Upr: DefaultNtCClassColors.OPN,
|
||||
OP03_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP04_Upr: DefaultNtCClassColors.OPN,
|
||||
OP04_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP05_Upr: DefaultNtCClassColors.OPN,
|
||||
OP05_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP06_Upr: DefaultNtCClassColors.OPN,
|
||||
OP06_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP07_Upr: DefaultNtCClassColors.OPN,
|
||||
OP07_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP08_Upr: DefaultNtCClassColors.OPN,
|
||||
OP08_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP09_Upr: DefaultNtCClassColors.OPN,
|
||||
OP09_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP10_Upr: DefaultNtCClassColors.OPN,
|
||||
OP10_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP11_Upr: DefaultNtCClassColors.OPN,
|
||||
OP11_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP12_Upr: DefaultNtCClassColors.OPN,
|
||||
OP12_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP13_Upr: DefaultNtCClassColors.OPN,
|
||||
OP13_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP14_Upr: DefaultNtCClassColors.OPN,
|
||||
OP14_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP15_Upr: DefaultNtCClassColors.OPN,
|
||||
OP15_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP16_Upr: DefaultNtCClassColors.OPN,
|
||||
OP16_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP17_Upr: DefaultNtCClassColors.OPN,
|
||||
OP17_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP18_Upr: DefaultNtCClassColors.OPN,
|
||||
OP18_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP19_Upr: DefaultNtCClassColors.OPN,
|
||||
OP19_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP20_Upr: DefaultNtCClassColors.OPN,
|
||||
OP20_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP21_Upr: DefaultNtCClassColors.OPN,
|
||||
OP21_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP22_Upr: DefaultNtCClassColors.OPN,
|
||||
OP22_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP23_Upr: DefaultNtCClassColors.OPN,
|
||||
OP23_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP24_Upr: DefaultNtCClassColors.OPN,
|
||||
OP24_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP25_Upr: DefaultNtCClassColors.OPN,
|
||||
OP25_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP26_Upr: DefaultNtCClassColors.OPN,
|
||||
OP26_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP27_Upr: DefaultNtCClassColors.OPN,
|
||||
OP27_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP28_Upr: DefaultNtCClassColors.OPN,
|
||||
OP28_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP29_Upr: DefaultNtCClassColors.OPN,
|
||||
OP29_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP30_Upr: DefaultNtCClassColors.OPN,
|
||||
OP30_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP31_Upr: DefaultNtCClassColors.OPN,
|
||||
OP31_Lwr: DefaultNtCClassColors.OPN,
|
||||
OPS1_Upr: DefaultNtCClassColors.OPN,
|
||||
OPS1_Lwr: DefaultNtCClassColors.OPN,
|
||||
OP1S_Upr: DefaultNtCClassColors.OPN,
|
||||
OP1S_Lwr: DefaultNtCClassColors.SYN,
|
||||
AAS1_Upr: DefaultNtCClassColors.SYN,
|
||||
AAS1_Lwr: DefaultNtCClassColors.A,
|
||||
AB1S_Upr: DefaultNtCClassColors.A,
|
||||
AB1S_Lwr: DefaultNtCClassColors.SYN,
|
||||
AB2S_Upr: DefaultNtCClassColors.A,
|
||||
AB2S_Lwr: DefaultNtCClassColors.SYN,
|
||||
BB1S_Upr: DefaultNtCClassColors.B,
|
||||
BB1S_Lwr: DefaultNtCClassColors.SYN,
|
||||
BB2S_Upr: DefaultNtCClassColors.B,
|
||||
BB2S_Lwr: DefaultNtCClassColors.SYN,
|
||||
BBS1_Upr: DefaultNtCClassColors.SYN,
|
||||
BBS1_Lwr: DefaultNtCClassColors.B,
|
||||
ZZ01_Upr: DefaultNtCClassColors.Z,
|
||||
ZZ01_Lwr: DefaultNtCClassColors.Z,
|
||||
ZZ02_Upr: DefaultNtCClassColors.Z,
|
||||
ZZ02_Lwr: DefaultNtCClassColors.Z,
|
||||
ZZ1S_Upr: DefaultNtCClassColors.Z,
|
||||
ZZ1S_Lwr: DefaultNtCClassColors.SYN,
|
||||
ZZ2S_Upr: DefaultNtCClassColors.Z,
|
||||
ZZ2S_Lwr: DefaultNtCClassColors.SYN,
|
||||
ZZS1_Upr: DefaultNtCClassColors.SYN,
|
||||
ZZS1_Lwr: DefaultNtCClassColors.Z,
|
||||
ZZS2_Upr: DefaultNtCClassColors.SYN,
|
||||
ZZS2_Lwr: DefaultNtCClassColors.Z,
|
||||
});
|
||||
|
||||
@@ -6,23 +6,22 @@
|
||||
*/
|
||||
|
||||
import { ConfalPyramidsColorThemeProvider } from './color';
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsRepresentationProvider } from './representation';
|
||||
import { ConfalPyramidsTypes } from './types';
|
||||
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
|
||||
import { Dnatco } from '../property';
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../../mol-state';
|
||||
import { Task } from '../../../mol-task';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export const DnatcoConfalPyramidsPreset = StructureRepresentationPresetProvider({
|
||||
export const ConfalPyramidsPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure-representation-confal-pyramids',
|
||||
display: {
|
||||
name: 'Confal Pyramids', group: 'Annotation',
|
||||
description: 'Schematic depiction of conformer class and confal value.',
|
||||
},
|
||||
isApplicable(a) {
|
||||
return a.data.models.length >= 1 && a.data.models.some(m => ConfalPyramids.isApplicable(m));
|
||||
return a.data.models.length >= 1 && a.data.models.some(m => Dnatco.isApplicable(m));
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
@@ -48,50 +47,7 @@ export const DnatcoConfalPyramidsPreset = StructureRepresentationPresetProvider(
|
||||
}
|
||||
});
|
||||
|
||||
export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean, showToolTip: boolean }>({
|
||||
name: 'dnatco-confal-pyramids-prop',
|
||||
category: 'custom-props',
|
||||
display: {
|
||||
name: 'Confal Pyramids',
|
||||
description: 'Schematic depiction of conformer class and confal value.',
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
|
||||
private provider = ConfalPyramidsProvider;
|
||||
|
||||
register(): void {
|
||||
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
|
||||
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.registerPreset(DnatcoConfalPyramidsPreset);
|
||||
}
|
||||
|
||||
update(p: { autoAttach: boolean, showToolTip: boolean }) {
|
||||
const updated = this.params.autoAttach !== p.autoAttach;
|
||||
this.params.autoAttach = p.autoAttach;
|
||||
this.params.showToolTip = p.showToolTip;
|
||||
this.ctx.customModelProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
|
||||
return updated;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
|
||||
|
||||
this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.unregisterPreset(DnatcoConfalPyramidsPreset);
|
||||
}
|
||||
},
|
||||
params: () => ({
|
||||
autoAttach: PD.Boolean(true),
|
||||
showToolTip: PD.Boolean(true)
|
||||
})
|
||||
});
|
||||
|
||||
export function confalPyramidLabel(halfPyramid: ConfalPyramidsTypes.HalfPyramid) {
|
||||
const { step } = halfPyramid;
|
||||
export function confalPyramidLabel(step: DnatcoTypes.Step) {
|
||||
return `
|
||||
<b>${step.auth_asym_id_1}</b> |
|
||||
<b>${step.label_comp_id_1} ${step.auth_seq_id_1}${step.PDB_ins_code_1}${step.label_alt_id_1.length > 0 ? ` (alt ${step.label_alt_id_1})` : ''}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ErrorColor, NtCColors } from '../color';
|
||||
import { ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { Dnatco } from '../property';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { ColorTheme } from '../../../mol-theme/color';
|
||||
@@ -19,215 +21,7 @@ import { ObjectKeys } from '../../../mol-util/type-helpers';
|
||||
|
||||
const Description = 'Assigns colors to confal pyramids';
|
||||
|
||||
const DefaultClassColors = {
|
||||
A: 0xFFC1C1,
|
||||
B: 0xC8CFFF,
|
||||
BII: 0x0059DA,
|
||||
miB: 0x3BE8FB,
|
||||
Z: 0x01F60E,
|
||||
IC: 0xFA5CFB,
|
||||
OPN: 0xE90000,
|
||||
SYN: 0xFFFF01,
|
||||
N: 0xF2F2F2,
|
||||
};
|
||||
const ErrorColor = Color(0xFFA10A);
|
||||
|
||||
const PyramidsColors = ColorMap({
|
||||
NANT_Upr: DefaultClassColors.N,
|
||||
NANT_Lwr: DefaultClassColors.N,
|
||||
AA00_Upr: DefaultClassColors.A,
|
||||
AA00_Lwr: DefaultClassColors.A,
|
||||
AA02_Upr: DefaultClassColors.A,
|
||||
AA02_Lwr: DefaultClassColors.A,
|
||||
AA03_Upr: DefaultClassColors.A,
|
||||
AA03_Lwr: DefaultClassColors.A,
|
||||
AA04_Upr: DefaultClassColors.A,
|
||||
AA04_Lwr: DefaultClassColors.A,
|
||||
AA08_Upr: DefaultClassColors.A,
|
||||
AA08_Lwr: DefaultClassColors.A,
|
||||
AA09_Upr: DefaultClassColors.A,
|
||||
AA09_Lwr: DefaultClassColors.A,
|
||||
AA01_Upr: DefaultClassColors.A,
|
||||
AA01_Lwr: DefaultClassColors.A,
|
||||
AA05_Upr: DefaultClassColors.A,
|
||||
AA05_Lwr: DefaultClassColors.A,
|
||||
AA06_Upr: DefaultClassColors.A,
|
||||
AA06_Lwr: DefaultClassColors.A,
|
||||
AA10_Upr: DefaultClassColors.A,
|
||||
AA10_Lwr: DefaultClassColors.A,
|
||||
AA11_Upr: DefaultClassColors.A,
|
||||
AA11_Lwr: DefaultClassColors.A,
|
||||
AA07_Upr: DefaultClassColors.A,
|
||||
AA07_Lwr: DefaultClassColors.A,
|
||||
AA12_Upr: DefaultClassColors.A,
|
||||
AA12_Lwr: DefaultClassColors.A,
|
||||
AA13_Upr: DefaultClassColors.A,
|
||||
AA13_Lwr: DefaultClassColors.A,
|
||||
AB01_Upr: DefaultClassColors.A,
|
||||
AB01_Lwr: DefaultClassColors.B,
|
||||
AB02_Upr: DefaultClassColors.A,
|
||||
AB02_Lwr: DefaultClassColors.B,
|
||||
AB03_Upr: DefaultClassColors.A,
|
||||
AB03_Lwr: DefaultClassColors.B,
|
||||
AB04_Upr: DefaultClassColors.A,
|
||||
AB04_Lwr: DefaultClassColors.B,
|
||||
AB05_Upr: DefaultClassColors.A,
|
||||
AB05_Lwr: DefaultClassColors.B,
|
||||
BA01_Upr: DefaultClassColors.B,
|
||||
BA01_Lwr: DefaultClassColors.A,
|
||||
BA05_Upr: DefaultClassColors.B,
|
||||
BA05_Lwr: DefaultClassColors.A,
|
||||
BA09_Upr: DefaultClassColors.B,
|
||||
BA09_Lwr: DefaultClassColors.A,
|
||||
BA08_Upr: DefaultClassColors.BII,
|
||||
BA08_Lwr: DefaultClassColors.A,
|
||||
BA10_Upr: DefaultClassColors.B,
|
||||
BA10_Lwr: DefaultClassColors.A,
|
||||
BA13_Upr: DefaultClassColors.BII,
|
||||
BA13_Lwr: DefaultClassColors.A,
|
||||
BA16_Upr: DefaultClassColors.BII,
|
||||
BA16_Lwr: DefaultClassColors.A,
|
||||
BA17_Upr: DefaultClassColors.BII,
|
||||
BA17_Lwr: DefaultClassColors.A,
|
||||
BB00_Upr: DefaultClassColors.B,
|
||||
BB00_Lwr: DefaultClassColors.B,
|
||||
BB01_Upr: DefaultClassColors.B,
|
||||
BB01_Lwr: DefaultClassColors.B,
|
||||
BB17_Upr: DefaultClassColors.B,
|
||||
BB17_Lwr: DefaultClassColors.B,
|
||||
BB02_Upr: DefaultClassColors.B,
|
||||
BB02_Lwr: DefaultClassColors.B,
|
||||
BB03_Upr: DefaultClassColors.B,
|
||||
BB03_Lwr: DefaultClassColors.B,
|
||||
BB11_Upr: DefaultClassColors.B,
|
||||
BB11_Lwr: DefaultClassColors.B,
|
||||
BB16_Upr: DefaultClassColors.B,
|
||||
BB16_Lwr: DefaultClassColors.B,
|
||||
BB04_Upr: DefaultClassColors.B,
|
||||
BB04_Lwr: DefaultClassColors.BII,
|
||||
BB05_Upr: DefaultClassColors.B,
|
||||
BB05_Lwr: DefaultClassColors.BII,
|
||||
BB07_Upr: DefaultClassColors.BII,
|
||||
BB07_Lwr: DefaultClassColors.BII,
|
||||
BB08_Upr: DefaultClassColors.BII,
|
||||
BB08_Lwr: DefaultClassColors.BII,
|
||||
BB10_Upr: DefaultClassColors.miB,
|
||||
BB10_Lwr: DefaultClassColors.miB,
|
||||
BB12_Upr: DefaultClassColors.miB,
|
||||
BB12_Lwr: DefaultClassColors.miB,
|
||||
BB13_Upr: DefaultClassColors.miB,
|
||||
BB13_Lwr: DefaultClassColors.miB,
|
||||
BB14_Upr: DefaultClassColors.miB,
|
||||
BB14_Lwr: DefaultClassColors.miB,
|
||||
BB15_Upr: DefaultClassColors.miB,
|
||||
BB15_Lwr: DefaultClassColors.miB,
|
||||
BB20_Upr: DefaultClassColors.miB,
|
||||
BB20_Lwr: DefaultClassColors.miB,
|
||||
IC01_Upr: DefaultClassColors.IC,
|
||||
IC01_Lwr: DefaultClassColors.IC,
|
||||
IC02_Upr: DefaultClassColors.IC,
|
||||
IC02_Lwr: DefaultClassColors.IC,
|
||||
IC03_Upr: DefaultClassColors.IC,
|
||||
IC03_Lwr: DefaultClassColors.IC,
|
||||
IC04_Upr: DefaultClassColors.IC,
|
||||
IC04_Lwr: DefaultClassColors.IC,
|
||||
IC05_Upr: DefaultClassColors.IC,
|
||||
IC05_Lwr: DefaultClassColors.IC,
|
||||
IC06_Upr: DefaultClassColors.IC,
|
||||
IC06_Lwr: DefaultClassColors.IC,
|
||||
IC07_Upr: DefaultClassColors.IC,
|
||||
IC07_Lwr: DefaultClassColors.IC,
|
||||
OP01_Upr: DefaultClassColors.OPN,
|
||||
OP01_Lwr: DefaultClassColors.OPN,
|
||||
OP02_Upr: DefaultClassColors.OPN,
|
||||
OP02_Lwr: DefaultClassColors.OPN,
|
||||
OP03_Upr: DefaultClassColors.OPN,
|
||||
OP03_Lwr: DefaultClassColors.OPN,
|
||||
OP04_Upr: DefaultClassColors.OPN,
|
||||
OP04_Lwr: DefaultClassColors.OPN,
|
||||
OP05_Upr: DefaultClassColors.OPN,
|
||||
OP05_Lwr: DefaultClassColors.OPN,
|
||||
OP06_Upr: DefaultClassColors.OPN,
|
||||
OP06_Lwr: DefaultClassColors.OPN,
|
||||
OP07_Upr: DefaultClassColors.OPN,
|
||||
OP07_Lwr: DefaultClassColors.OPN,
|
||||
OP08_Upr: DefaultClassColors.OPN,
|
||||
OP08_Lwr: DefaultClassColors.OPN,
|
||||
OP09_Upr: DefaultClassColors.OPN,
|
||||
OP09_Lwr: DefaultClassColors.OPN,
|
||||
OP10_Upr: DefaultClassColors.OPN,
|
||||
OP10_Lwr: DefaultClassColors.OPN,
|
||||
OP11_Upr: DefaultClassColors.OPN,
|
||||
OP11_Lwr: DefaultClassColors.OPN,
|
||||
OP12_Upr: DefaultClassColors.OPN,
|
||||
OP12_Lwr: DefaultClassColors.OPN,
|
||||
OP13_Upr: DefaultClassColors.OPN,
|
||||
OP13_Lwr: DefaultClassColors.OPN,
|
||||
OP14_Upr: DefaultClassColors.OPN,
|
||||
OP14_Lwr: DefaultClassColors.OPN,
|
||||
OP15_Upr: DefaultClassColors.OPN,
|
||||
OP15_Lwr: DefaultClassColors.OPN,
|
||||
OP16_Upr: DefaultClassColors.OPN,
|
||||
OP16_Lwr: DefaultClassColors.OPN,
|
||||
OP17_Upr: DefaultClassColors.OPN,
|
||||
OP17_Lwr: DefaultClassColors.OPN,
|
||||
OP18_Upr: DefaultClassColors.OPN,
|
||||
OP18_Lwr: DefaultClassColors.OPN,
|
||||
OP19_Upr: DefaultClassColors.OPN,
|
||||
OP19_Lwr: DefaultClassColors.OPN,
|
||||
OP20_Upr: DefaultClassColors.OPN,
|
||||
OP20_Lwr: DefaultClassColors.OPN,
|
||||
OP21_Upr: DefaultClassColors.OPN,
|
||||
OP21_Lwr: DefaultClassColors.OPN,
|
||||
OP22_Upr: DefaultClassColors.OPN,
|
||||
OP22_Lwr: DefaultClassColors.OPN,
|
||||
OP23_Upr: DefaultClassColors.OPN,
|
||||
OP23_Lwr: DefaultClassColors.OPN,
|
||||
OP24_Upr: DefaultClassColors.OPN,
|
||||
OP24_Lwr: DefaultClassColors.OPN,
|
||||
OP25_Upr: DefaultClassColors.OPN,
|
||||
OP25_Lwr: DefaultClassColors.OPN,
|
||||
OP26_Upr: DefaultClassColors.OPN,
|
||||
OP26_Lwr: DefaultClassColors.OPN,
|
||||
OP27_Upr: DefaultClassColors.OPN,
|
||||
OP27_Lwr: DefaultClassColors.OPN,
|
||||
OP28_Upr: DefaultClassColors.OPN,
|
||||
OP28_Lwr: DefaultClassColors.OPN,
|
||||
OP29_Upr: DefaultClassColors.OPN,
|
||||
OP29_Lwr: DefaultClassColors.OPN,
|
||||
OP30_Upr: DefaultClassColors.OPN,
|
||||
OP30_Lwr: DefaultClassColors.OPN,
|
||||
OP31_Upr: DefaultClassColors.OPN,
|
||||
OP31_Lwr: DefaultClassColors.OPN,
|
||||
OPS1_Upr: DefaultClassColors.OPN,
|
||||
OPS1_Lwr: DefaultClassColors.OPN,
|
||||
OP1S_Upr: DefaultClassColors.OPN,
|
||||
OP1S_Lwr: DefaultClassColors.SYN,
|
||||
AAS1_Upr: DefaultClassColors.SYN,
|
||||
AAS1_Lwr: DefaultClassColors.A,
|
||||
AB1S_Upr: DefaultClassColors.A,
|
||||
AB1S_Lwr: DefaultClassColors.SYN,
|
||||
AB2S_Upr: DefaultClassColors.A,
|
||||
AB2S_Lwr: DefaultClassColors.SYN,
|
||||
BB1S_Upr: DefaultClassColors.B,
|
||||
BB1S_Lwr: DefaultClassColors.SYN,
|
||||
BB2S_Upr: DefaultClassColors.B,
|
||||
BB2S_Lwr: DefaultClassColors.SYN,
|
||||
BBS1_Upr: DefaultClassColors.SYN,
|
||||
BBS1_Lwr: DefaultClassColors.B,
|
||||
ZZ01_Upr: DefaultClassColors.Z,
|
||||
ZZ01_Lwr: DefaultClassColors.Z,
|
||||
ZZ02_Upr: DefaultClassColors.Z,
|
||||
ZZ02_Lwr: DefaultClassColors.Z,
|
||||
ZZ1S_Upr: DefaultClassColors.Z,
|
||||
ZZ1S_Lwr: DefaultClassColors.SYN,
|
||||
ZZ2S_Upr: DefaultClassColors.Z,
|
||||
ZZ2S_Lwr: DefaultClassColors.SYN,
|
||||
ZZS1_Upr: DefaultClassColors.SYN,
|
||||
ZZS1_Lwr: DefaultClassColors.Z,
|
||||
ZZS2_Upr: DefaultClassColors.SYN,
|
||||
ZZS2_Lwr: DefaultClassColors.Z,
|
||||
});
|
||||
const PyramidsColors = ColorMap({ ...NtCColors });
|
||||
type PyramidsColors = typeof PyramidsColors;
|
||||
|
||||
export const ConfalPyramidsColorThemeParams = {
|
||||
@@ -272,7 +66,7 @@ export const ConfalPyramidsColorThemeProvider: ColorTheme.Provider<ConfalPyramid
|
||||
factory: ConfalPyramidsColorTheme,
|
||||
getParams: getConfalPyramidsColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(ConfalPyramidsColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => ConfalPyramids.isApplicable(m)),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => Dnatco.isApplicable(m)),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ConfalPyramidsProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
|
||||
detach: (data) => data.structure && ConfalPyramidsProvider.ref(data.structure.models[0], false)
|
||||
|
||||
@@ -5,90 +5,18 @@
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { Column, Table } from '../../../mol-data/db';
|
||||
import { toTable } from '../../../mol-io/reader/cif/schema';
|
||||
import { Dnatco, DnatcoParams, DnatcoSteps } from '../property';
|
||||
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
|
||||
import { Model } from '../../../mol-model/structure';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
|
||||
import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
|
||||
|
||||
export type ConfalPyramids = PropertyWrapper<CPT.Steps | undefined>;
|
||||
|
||||
export namespace ConfalPyramids {
|
||||
export const Schema = {
|
||||
ndb_struct_ntc_step: {
|
||||
id: Column.Schema.int,
|
||||
name: Column.Schema.str,
|
||||
PDB_model_number: Column.Schema.int,
|
||||
label_entity_id_1: Column.Schema.int,
|
||||
label_asym_id_1: Column.Schema.str,
|
||||
label_seq_id_1: Column.Schema.int,
|
||||
label_comp_id_1: Column.Schema.str,
|
||||
label_alt_id_1: Column.Schema.str,
|
||||
label_entity_id_2: Column.Schema.int,
|
||||
label_asym_id_2: Column.Schema.str,
|
||||
label_seq_id_2: Column.Schema.int,
|
||||
label_comp_id_2: Column.Schema.str,
|
||||
label_alt_id_2: Column.Schema.str,
|
||||
auth_asym_id_1: Column.Schema.str,
|
||||
auth_seq_id_1: Column.Schema.int,
|
||||
auth_asym_id_2: Column.Schema.str,
|
||||
auth_seq_id_2: Column.Schema.int,
|
||||
PDB_ins_code_1: Column.Schema.str,
|
||||
PDB_ins_code_2: Column.Schema.str,
|
||||
},
|
||||
ndb_struct_ntc_step_summary: {
|
||||
step_id: Column.Schema.int,
|
||||
assigned_CANA: Column.Schema.str,
|
||||
assigned_NtC: Column.Schema.str,
|
||||
confal_score: Column.Schema.int,
|
||||
euclidean_distance_NtC_ideal: Column.Schema.float,
|
||||
cartesian_rmsd_closest_NtC_representative: Column.Schema.float,
|
||||
closest_CANA: Column.Schema.str,
|
||||
closest_NtC: Column.Schema.str,
|
||||
closest_step_golden: Column.Schema.str
|
||||
}
|
||||
};
|
||||
export type Schema = typeof Schema;
|
||||
|
||||
export async function fromCif(ctx: CustomProperty.Context, model: Model, props: ConfalPyramidsProps): Promise<CustomProperty.Data<ConfalPyramids>> {
|
||||
const info = PropertyWrapper.createInfo();
|
||||
const data = getCifData(model);
|
||||
if (data === undefined) return { value: { info, data: undefined } };
|
||||
|
||||
const fromCif = createPyramidsFromCif(model, data.steps, data.stepsSummary);
|
||||
return { value: { info, data: fromCif } };
|
||||
}
|
||||
|
||||
function getCifData(model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) throw new Error('Data format must be mmCIF');
|
||||
if (!hasNdbStructNtcCategories(model)) return undefined;
|
||||
return {
|
||||
steps: toTable(Schema.ndb_struct_ntc_step, model.sourceData.data.frame.categories.ndb_struct_ntc_step),
|
||||
stepsSummary: toTable(Schema.ndb_struct_ntc_step_summary, model.sourceData.data.frame.categories.ndb_struct_ntc_step_summary)
|
||||
};
|
||||
}
|
||||
|
||||
function hasNdbStructNtcCategories(model: Model): boolean {
|
||||
if (!MmcifFormat.is(model.sourceData)) return false;
|
||||
const names = (model.sourceData).data.frame.categoryNames;
|
||||
return names.includes('ndb_struct_ntc_step') && names.includes('ndb_struct_ntc_step_summary');
|
||||
}
|
||||
|
||||
export function isApplicable(model?: Model): boolean {
|
||||
return !!model && hasNdbStructNtcCategories(model);
|
||||
}
|
||||
}
|
||||
|
||||
export const ConfalPyramidsParams = {};
|
||||
export const ConfalPyramidsParams = { ...DnatcoParams };
|
||||
export type ConfalPyramidsParams = typeof ConfalPyramidsParams;
|
||||
export type ConfalPyramidsProps = PD.Values<ConfalPyramidsParams>;
|
||||
|
||||
export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramidsParams, ConfalPyramids> = CustomModelProperty.createProvider({
|
||||
export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramidsParams, DnatcoSteps> = CustomModelProperty.createProvider({
|
||||
label: 'Confal Pyramids',
|
||||
descriptor: CustomPropertyDescriptor({
|
||||
name: 'confal_pyramids',
|
||||
@@ -96,94 +24,9 @@ export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramids
|
||||
type: 'static',
|
||||
defaultParams: ConfalPyramidsParams,
|
||||
getParams: (data: Model) => ConfalPyramidsParams,
|
||||
isApplicable: (data: Model) => ConfalPyramids.isApplicable(data),
|
||||
isApplicable: (data: Model) => Dnatco.isApplicable(data),
|
||||
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<ConfalPyramidsProps>) => {
|
||||
const p = { ...PD.getDefaultValues(ConfalPyramidsParams), ...props };
|
||||
return ConfalPyramids.fromCif(ctx, data, p);
|
||||
return Dnatco.fromCif(ctx, data, p);
|
||||
}
|
||||
});
|
||||
|
||||
type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>;
|
||||
|
||||
function createPyramidsFromCif(
|
||||
model: Model,
|
||||
cifSteps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
|
||||
stepsSummary: StepsSummaryTable
|
||||
): CPT.Steps {
|
||||
const steps = new Array<CPT.Step>();
|
||||
const mapping = new Array<CPT.MappedChains>();
|
||||
|
||||
const {
|
||||
id, PDB_model_number, name,
|
||||
auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
|
||||
auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
|
||||
_rowCount
|
||||
} = cifSteps;
|
||||
|
||||
if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
|
||||
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
const {
|
||||
NtC,
|
||||
confal_score,
|
||||
rmsd
|
||||
} = getSummaryData(id.value(i), i, stepsSummary);
|
||||
const modelNum = PDB_model_number.value(i);
|
||||
const chainId = auth_asym_id_1.value(i);
|
||||
const seqId = auth_seq_id_1.value(i);
|
||||
const modelIdx = modelNum - 1;
|
||||
|
||||
if (mapping.length <= modelIdx || !mapping[modelIdx])
|
||||
mapping[modelIdx] = new Map<string, CPT.MappedResidues>();
|
||||
|
||||
const step = {
|
||||
PDB_model_number: modelNum,
|
||||
name: name.value(i),
|
||||
auth_asym_id_1: chainId,
|
||||
auth_seq_id_1: seqId,
|
||||
label_comp_id_1: label_comp_id_1.value(i),
|
||||
label_alt_id_1: label_alt_id_1.value(i),
|
||||
PDB_ins_code_1: PDB_ins_code_1.value(i),
|
||||
auth_asym_id_2: auth_asym_id_2.value(i),
|
||||
auth_seq_id_2: auth_seq_id_2.value(i),
|
||||
label_comp_id_2: label_comp_id_2.value(i),
|
||||
label_alt_id_2: label_alt_id_2.value(i),
|
||||
PDB_ins_code_2: PDB_ins_code_2.value(i),
|
||||
confal_score,
|
||||
NtC,
|
||||
rmsd,
|
||||
};
|
||||
|
||||
steps.push(step);
|
||||
|
||||
const mappedChains = mapping[modelIdx];
|
||||
const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>();
|
||||
const stepsForResidue = residuesOnChain.get(seqId) ?? [];
|
||||
stepsForResidue.push(steps.length - 1);
|
||||
|
||||
residuesOnChain.set(seqId, stepsForResidue);
|
||||
mappedChains.set(chainId, residuesOnChain);
|
||||
mapping[modelIdx] = mappedChains;
|
||||
}
|
||||
|
||||
return { steps, mapping };
|
||||
}
|
||||
|
||||
function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) {
|
||||
const {
|
||||
step_id,
|
||||
confal_score,
|
||||
assigned_NtC,
|
||||
cartesian_rmsd_closest_NtC_representative,
|
||||
} = stepsSummary;
|
||||
|
||||
// Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
|
||||
for (let j = i; j < stepsSummary._rowCount; j++) {
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
// Safety net for cases where the previous assumption is not met
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
throw new Error('Inconsistent mmCIF data');
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsIterator } from './util';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { Dnatco } from '../property';
|
||||
import { Interval } from '../../../mol-data/int';
|
||||
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
|
||||
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
|
||||
@@ -87,6 +88,8 @@ function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Str
|
||||
const it = new ConfalPyramidsIterator(structure, unit);
|
||||
while (it.hasNext) {
|
||||
const allPoints = it.move();
|
||||
if (!allPoints)
|
||||
continue;
|
||||
|
||||
for (const points of allPoints) {
|
||||
const { O3, P, OP1, OP2, O5, confalScore } = points;
|
||||
@@ -150,9 +153,7 @@ function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGro
|
||||
if (halfPyramidsCount <= groupId) return EmptyLoci;
|
||||
|
||||
const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
|
||||
const step = data.steps[idx];
|
||||
|
||||
return CPT.Loci({ step, isLower: groupId % 2 === 1 }, [{}]);
|
||||
return CPT.Loci(data.steps, [idx]);
|
||||
}
|
||||
|
||||
function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
|
||||
@@ -197,7 +198,7 @@ export const ConfalPyramidsRepresentationProvider = StructureRepresentationProvi
|
||||
defaultValues: PD.getDefaultValues(ConfalPyramidsParams),
|
||||
defaultColorTheme: { name: 'confal-pyramids' },
|
||||
defaultSizeTheme: { name: 'uniform' },
|
||||
isApplicable: (structure: Structure) => structure.models.some(m => ConfalPyramids.isApplicable(m)),
|
||||
isApplicable: (structure: Structure) => structure.models.some(m => Dnatco.isApplicable(m)),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, structure: Structure) => ConfalPyramidsProvider.attach(ctx, structure.model, void 0, true),
|
||||
detach: (data) => ConfalPyramidsProvider.ref(data.model, false),
|
||||
|
||||
@@ -5,61 +5,29 @@
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { DataLocation } from '../../../mol-model/location';
|
||||
import { DataLoci } from '../../../mol-model/loci';
|
||||
import { confalPyramidLabel } from './behavior';
|
||||
|
||||
export namespace ConfalPyramidsTypes {
|
||||
export const DataTag = 'dnatco-confal-half-pyramid';
|
||||
export interface Location extends DataLocation<DnatcoTypes.HalfStep, {}> {}
|
||||
|
||||
export type Step = {
|
||||
PDB_model_number: number,
|
||||
name: string,
|
||||
auth_asym_id_1: string,
|
||||
auth_seq_id_1: number,
|
||||
label_comp_id_1: string,
|
||||
label_alt_id_1: string,
|
||||
PDB_ins_code_1: string,
|
||||
auth_asym_id_2: string,
|
||||
auth_seq_id_2: number,
|
||||
label_comp_id_2: string,
|
||||
label_alt_id_2: string,
|
||||
PDB_ins_code_2: string,
|
||||
confal_score: number,
|
||||
NtC: string,
|
||||
rmsd: number,
|
||||
}
|
||||
|
||||
export type MappedChains = Map<string, MappedResidues>;
|
||||
export type MappedResidues = Map<number, number[]>;
|
||||
|
||||
export interface Steps {
|
||||
steps: Array<Step>,
|
||||
mapping: MappedChains[],
|
||||
}
|
||||
|
||||
export interface HalfPyramid {
|
||||
step: Step,
|
||||
isLower: boolean,
|
||||
}
|
||||
|
||||
export interface Location extends DataLocation<HalfPyramid, {}> {}
|
||||
|
||||
export function Location(step: Step, isLower: boolean) {
|
||||
return DataLocation(DataTag, { step, isLower }, {});
|
||||
export function Location(step: DnatcoTypes.Step, isLower: boolean) {
|
||||
return DataLocation(DnatcoTypes.DataTag, { step, isLower }, {});
|
||||
}
|
||||
|
||||
export function isLocation(x: any): x is Location {
|
||||
return !!x && x.kind === 'data-location' && x.tag === DataTag;
|
||||
return !!x && x.kind === 'data-location' && x.tag === DnatcoTypes.DataTag;
|
||||
}
|
||||
|
||||
export interface Loci extends DataLoci<HalfPyramid, {}> {}
|
||||
export interface Loci extends DataLoci<DnatcoTypes.Step[], number> {}
|
||||
|
||||
export function Loci(data: HalfPyramid, elements: ReadonlyArray<{}>): Loci {
|
||||
return DataLoci(DataTag, data, elements, undefined, () => confalPyramidLabel(data));
|
||||
export function Loci(data: DnatcoTypes.Step[], elements: ReadonlyArray<number>): Loci {
|
||||
return DataLoci(DnatcoTypes.DataTag, data, elements, undefined, () => elements[0] !== undefined ? confalPyramidLabel(data[elements[0]]) : '');
|
||||
}
|
||||
|
||||
export function isLoci(x: any): x is Loci {
|
||||
return !!x && x.kind === 'data-loci' && x.tag === DataTag;
|
||||
return !!x && x.kind === 'data-loci' && x.tag === DnatcoTypes.DataTag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { DnatcoUtil } from '../util';
|
||||
import { Segmentation } from '../../../mol-data/int';
|
||||
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure';
|
||||
|
||||
type Residue = Segmentation.Segment<ResidueIndex>;
|
||||
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
|
||||
|
||||
export type Pyramid = {
|
||||
O3: ElementIndex,
|
||||
@@ -22,31 +21,17 @@ export type Pyramid = {
|
||||
stepIdx: number,
|
||||
};
|
||||
|
||||
const EmptyStepIndices = new Array<number>();
|
||||
|
||||
function copyResidue(r?: Residue) {
|
||||
return r ? { index: r.index, start: r.start, end: r.end } : void 0;
|
||||
}
|
||||
|
||||
function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string): ElementIndex {
|
||||
for (let eI = residue.start; eI < residue.end; eI++) {
|
||||
loc.element = loc.unit.elements[eI];
|
||||
const elName = StructureProperties.atom.label_atom_id(loc);
|
||||
const elAltId = StructureProperties.atom.label_alt_id(loc);
|
||||
|
||||
if (names.includes(elName) && (elAltId === altId || elAltId.length === 0))
|
||||
return loc.element;
|
||||
}
|
||||
|
||||
return -1 as ElementIndex;
|
||||
}
|
||||
|
||||
function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue, altIdOne: string, altIdTwo: string, confalScore: number, stepIdx: number): Pyramid {
|
||||
const O3 = getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne);
|
||||
const P = getAtomIndex(loc, two, ['P'], altIdTwo);
|
||||
const OP1 = getAtomIndex(loc, two, ['OP1'], altIdTwo);
|
||||
const OP2 = getAtomIndex(loc, two, ['OP2'], altIdTwo);
|
||||
const O5 = getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo);
|
||||
function getPyramid(
|
||||
loc: StructureElement.Location,
|
||||
one: DnatcoUtil.Residue, two: DnatcoUtil.Residue,
|
||||
altIdOne: string, altIdTwo: string,
|
||||
insCodeOne: string, insCodeTwo: string,
|
||||
confalScore: number, stepIdx: number): Pyramid {
|
||||
const O3 = DnatcoUtil.getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne, insCodeOne);
|
||||
const P = DnatcoUtil.getAtomIndex(loc, two, ['P'], altIdTwo, insCodeTwo);
|
||||
const OP1 = DnatcoUtil.getAtomIndex(loc, two, ['OP1'], altIdTwo, insCodeTwo);
|
||||
const OP2 = DnatcoUtil.getAtomIndex(loc, two, ['OP2'], altIdTwo, insCodeTwo);
|
||||
const O5 = DnatcoUtil.getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo, insCodeTwo);
|
||||
|
||||
return { O3, P, OP1, OP2, O5, confalScore, stepIdx };
|
||||
}
|
||||
@@ -54,39 +39,29 @@ function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue,
|
||||
export class ConfalPyramidsIterator {
|
||||
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
|
||||
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
|
||||
private residueOne?: Residue;
|
||||
private residueTwo: Residue;
|
||||
private data?: CPT.Steps;
|
||||
private residueOne?: DnatcoUtil.Residue;
|
||||
private residueTwo: DnatcoUtil.Residue;
|
||||
private data?: DnatcoTypes.Steps;
|
||||
private loc: StructureElement.Location;
|
||||
|
||||
private getStepIndices(r: Residue) {
|
||||
this.loc.element = this.loc.unit.elements[r.start];
|
||||
|
||||
const modelIdx = StructureProperties.unit.model_num(this.loc) - 1;
|
||||
const chainId = StructureProperties.chain.auth_asym_id(this.loc);
|
||||
const seqId = StructureProperties.residue.auth_seq_id(this.loc);
|
||||
|
||||
const chains = this.data!.mapping[modelIdx];
|
||||
if (!chains) return EmptyStepIndices;
|
||||
const residues = chains.get(chainId);
|
||||
if (!residues) return EmptyStepIndices;
|
||||
return residues.get(seqId) ?? EmptyStepIndices;
|
||||
}
|
||||
|
||||
private moveStep() {
|
||||
this.residueOne = copyResidue(this.residueTwo);
|
||||
this.residueTwo = copyResidue(this.residueIt.move())!;
|
||||
this.residueOne = DnatcoUtil.copyResidue(this.residueTwo);
|
||||
this.residueTwo = DnatcoUtil.copyResidue(this.residueIt.move())!;
|
||||
|
||||
// Check for discontinuity
|
||||
if (this.residueTwo.index !== (this.residueOne!.index + 1))
|
||||
return void 0;
|
||||
|
||||
return this.toPyramids(this.residueOne!, this.residueTwo);
|
||||
}
|
||||
|
||||
private toPyramids(one: Residue, two: Residue) {
|
||||
const indices = this.getStepIndices(one);
|
||||
private toPyramids(one: DnatcoUtil.Residue, two: DnatcoUtil.Residue) {
|
||||
const indices = DnatcoUtil.getStepIndices(this.data!, this.loc, one);
|
||||
|
||||
const points = [];
|
||||
for (const idx of indices) {
|
||||
const step = this.data!.steps[idx];
|
||||
points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.confal_score, idx));
|
||||
points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.PDB_ins_code_1, step.PDB_ins_code_2, step.confal_score, idx));
|
||||
}
|
||||
|
||||
return points;
|
||||
@@ -121,6 +96,8 @@ export class ConfalPyramidsIterator {
|
||||
return this.moveStep();
|
||||
} else {
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
if (this.residueIt.hasNext)
|
||||
this.residueTwo = this.residueIt.move();
|
||||
return this.moveStep();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
export { DnatcoConfalPyramids } from './confal-pyramids/behavior';
|
||||
export { DnatcoNtCs } from './behavior';
|
||||
|
||||
57
src/extensions/dnatco/ntc-tube/behavior.ts
Normal file
57
src/extensions/dnatco/ntc-tube/behavior.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { NtCTubeColorThemeProvider } from './color';
|
||||
import { NtCTubeProvider } from './property';
|
||||
import { NtCTubeRepresentationProvider } from './representation';
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { Dnatco } from '../property';
|
||||
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../../mol-state';
|
||||
import { Task } from '../../../mol-task';
|
||||
|
||||
export const NtCTubePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure-representation-ntc-tube',
|
||||
display: {
|
||||
name: 'NtC Tube', group: 'Annotation',
|
||||
description: 'NtC Tube',
|
||||
},
|
||||
isApplicable(a) {
|
||||
return a.data.models.length >= 1 && a.data.models.some(m => Dnatco.isApplicable(m));
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const model = structureCell?.obj?.data.model;
|
||||
if (!structureCell || !model) return {};
|
||||
|
||||
await plugin.runTask(Task.create('NtC tube', async runtime => {
|
||||
await NtCTubeProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
|
||||
}));
|
||||
|
||||
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params }, plugin);
|
||||
|
||||
const tube = await plugin.builders.structure.tryCreateComponentStatic(structureCell, 'nucleic', { label: 'NtC Tube' });
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
|
||||
let tubeRepr;
|
||||
if (representations)
|
||||
tubeRepr = builder.buildRepresentation(update, tube, { type: NtCTubeRepresentationProvider, typeParams, color: NtCTubeColorThemeProvider }, { tag: 'ntc-tube' });
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
return { components: { ...components, tube }, representations: { ...representations, tubeRepr } };
|
||||
}
|
||||
});
|
||||
|
||||
export function NtCTubeSegmentLabel(step: DnatcoTypes.Step) {
|
||||
return `
|
||||
<b>${step.auth_asym_id_1}</b> |
|
||||
<b>${step.label_comp_id_1} ${step.auth_seq_id_1}${step.PDB_ins_code_1}${step.label_alt_id_1.length > 0 ? ` (alt ${step.label_alt_id_1})` : ''}
|
||||
${step.label_comp_id_2} ${step.auth_seq_id_2}${step.PDB_ins_code_2}${step.label_alt_id_2.length > 0 ? ` (alt ${step.label_alt_id_2})` : ''} </b><br />
|
||||
<i>NtC:</i> ${step.NtC} | <i>Confal score:</i> ${step.confal_score} | <i>RMSD:</i> ${step.rmsd.toFixed(2)}
|
||||
`;
|
||||
}
|
||||
90
src/extensions/dnatco/ntc-tube/color.ts
Normal file
90
src/extensions/dnatco/ntc-tube/color.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ErrorColor, NtCColors } from '../color';
|
||||
import { NtCTubeProvider } from './property';
|
||||
import { NtCTubeTypes as NTT } from './types';
|
||||
import { Dnatco } from '../property';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { ColorTheme } from '../../../mol-theme/color';
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { Color, ColorMap } from '../../../mol-util/color';
|
||||
import { getColorMapParams } from '../../../mol-util/color/params';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { TableLegend } from '../../../mol-util/legend';
|
||||
import { ObjectKeys } from '../../../mol-util/type-helpers';
|
||||
|
||||
const Description = 'Assigns colors to NtC Tube segments';
|
||||
|
||||
const NtCTubeColors = ColorMap({
|
||||
...NtCColors,
|
||||
residueMarker: Color(0x222222),
|
||||
stepBoundaryMarker: Color(0x656565),
|
||||
});
|
||||
type NtCTubeColors = typeof NtCTubeColors;
|
||||
|
||||
export const NtCTubeColorThemeParams = {
|
||||
colors: PD.MappedStatic('default', {
|
||||
'default': PD.EmptyGroup(),
|
||||
'custom': PD.Group(getColorMapParams(NtCTubeColors))
|
||||
}),
|
||||
markResidueBoundaries: PD.Boolean(true),
|
||||
markSegmentBoundaries: PD.Boolean(true),
|
||||
};
|
||||
export type NtCTubeColorThemeParams = typeof NtCTubeColorThemeParams;
|
||||
|
||||
export function getNtCTubeColorThemeParams(ctx: ThemeDataContext) {
|
||||
return PD.clone(NtCTubeColorThemeParams);
|
||||
}
|
||||
|
||||
export function NtCTubeColorTheme(ctx: ThemeDataContext, props: PD.Values<NtCTubeColorThemeParams>): ColorTheme<NtCTubeColorThemeParams> {
|
||||
const colorMap = props.colors.name === 'default' ? NtCTubeColors : props.colors.params;
|
||||
|
||||
function color(location: Location, isSecondary: boolean): Color {
|
||||
if (NTT.isLocation(location)) {
|
||||
const { data } = location;
|
||||
const { step, kind } = data;
|
||||
let key;
|
||||
if (kind === 'upper')
|
||||
key = step.NtC + '_Upr' as keyof NtCTubeColors;
|
||||
else if (kind === 'lower')
|
||||
key = step.NtC + '_Lwr' as keyof NtCTubeColors;
|
||||
else if (kind === 'residue-boundary')
|
||||
key = (!props.markResidueBoundaries ? step.NtC + '_Lwr' : 'residueMarker') as keyof NtCTubeColors;
|
||||
else /* segment-boundary */
|
||||
key = (!props.markSegmentBoundaries ? step.NtC + '_Lwr' : 'stepBoundaryMarker') as keyof NtCTubeColors;
|
||||
|
||||
return colorMap[key] ?? ErrorColor;
|
||||
}
|
||||
|
||||
return ErrorColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: NtCTubeColorTheme,
|
||||
granularity: 'group',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend: TableLegend(ObjectKeys(colorMap).map(k => [k.replace('_', ' '), colorMap[k]] as [string, Color]).concat([['Error', ErrorColor]])),
|
||||
};
|
||||
}
|
||||
|
||||
export const NtCTubeColorThemeProvider: ColorTheme.Provider<NtCTubeColorThemeParams, 'ntc-tube'> = {
|
||||
name: 'ntc-tube',
|
||||
label: 'NtC Tube',
|
||||
category: ColorTheme.Category.Residue,
|
||||
factory: NtCTubeColorTheme,
|
||||
getParams: getNtCTubeColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(NtCTubeColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => Dnatco.isApplicable(m)),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? NtCTubeProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
|
||||
detach: (data) => data.structure && NtCTubeProvider.ref(data.structure.models[0], false)
|
||||
}
|
||||
};
|
||||
44
src/extensions/dnatco/ntc-tube/property.ts
Normal file
44
src/extensions/dnatco/ntc-tube/property.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { NtCTubeTypes as NTT } from './types';
|
||||
import { Dnatco, DnatcoParams } from '../property';
|
||||
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
|
||||
import { Model } from '../../../mol-model/structure';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
|
||||
import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export const NtCTubeParams = { ...DnatcoParams };
|
||||
export type NtCTubeParams = typeof NtCTubeParams;
|
||||
export type NtCTubeProps = PD.Values<NtCTubeParams>;
|
||||
export type NtCTubeData = PropertyWrapper<NTT.Data | undefined>;
|
||||
|
||||
async function fromCif(ctx: CustomProperty.Context, model: Model, props: NtCTubeProps): Promise<CustomProperty.Data<NtCTubeData>> {
|
||||
const info = PropertyWrapper.createInfo();
|
||||
const data = Dnatco.getCifData(model);
|
||||
if (data === undefined) return { value: { info, data: undefined } };
|
||||
|
||||
const steps = Dnatco.getStepsFromCif(model, data.steps, data.stepsSummary);
|
||||
return { value: { info, data: { data: steps } } };
|
||||
}
|
||||
|
||||
export const NtCTubeProvider: CustomModelProperty.Provider<NtCTubeParams, NtCTubeData> = CustomModelProperty.createProvider({
|
||||
label: 'NtC Tube',
|
||||
descriptor: CustomPropertyDescriptor({
|
||||
name: 'ntc-tube',
|
||||
}),
|
||||
type: 'static',
|
||||
defaultParams: NtCTubeParams,
|
||||
getParams: (data: Model) => NtCTubeParams,
|
||||
isApplicable: (data: Model) => Dnatco.isApplicable(data),
|
||||
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<NtCTubeProps>) => {
|
||||
const p = { ...PD.getDefaultValues(NtCTubeParams), ...props };
|
||||
return fromCif(ctx, data, p);
|
||||
}
|
||||
});
|
||||
454
src/extensions/dnatco/ntc-tube/representation.ts
Normal file
454
src/extensions/dnatco/ntc-tube/representation.ts
Normal file
@@ -0,0 +1,454 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { NtCTubeProvider } from './property';
|
||||
import { NtCTubeSegmentsIterator } from './util';
|
||||
import { NtCTubeTypes as NTT } from './types';
|
||||
import { Dnatco } from '../property';
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { DnatcoUtil } from '../util';
|
||||
import { Interval } from '../../../mol-data/int';
|
||||
import { BaseGeometry, VisualQuality } from '../../../mol-geo/geometry/base';
|
||||
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
|
||||
import { addFixedCountDashedCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
|
||||
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { addTube } from '../../../mol-geo/geometry/mesh/builder/tube';
|
||||
import { PickingId } from '../../../mol-geo/geometry/picking';
|
||||
import { CylinderProps } from '../../../mol-geo/primitive/cylinder';
|
||||
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
import { Sphere3D } from '../../../mol-math/geometry/primitives/sphere3d';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { smoothstep } from '../../../mol-math/interpolate';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
import { EmptyLoci, Loci } from '../../../mol-model/loci';
|
||||
import { Structure, StructureElement, Unit } from '../../../mol-model/structure';
|
||||
import { structureUnion } from '../../../mol-model/structure/query/utils/structure-set';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
|
||||
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
|
||||
import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
|
||||
import { createCurveSegmentState, CurveSegmentState } from '../../../mol-repr/structure/visual/util/polymer';
|
||||
import { getStructureQuality, VisualUpdateState } from '../../../mol-repr/util';
|
||||
import { VisualContext } from '../../../mol-repr/visual';
|
||||
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
|
||||
import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
const v3add = Vec3.add;
|
||||
const v3copy = Vec3.copy;
|
||||
const v3cross = Vec3.cross;
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
const v3matchDirection = Vec3.matchDirection;
|
||||
const v3normalize = Vec3.normalize;
|
||||
const v3orthogonalize = Vec3.orthogonalize;
|
||||
const v3scale = Vec3.scale;
|
||||
const v3slerp = Vec3.slerp;
|
||||
const v3spline = Vec3.spline;
|
||||
const v3sub = Vec3.sub;
|
||||
const v3toArray = Vec3.toArray;
|
||||
|
||||
const NtCTubeMeshParams = {
|
||||
...UnitsMeshParams,
|
||||
linearSegments: PD.Numeric(4, { min: 2, max: 8, step: 1 }, BaseGeometry.CustomQualityParamInfo),
|
||||
radialSegments: PD.Numeric(22, { min: 4, max: 56, step: 2 }, BaseGeometry.CustomQualityParamInfo),
|
||||
residueMarkerWidth: PD.Numeric(0.05, { min: 0.01, max: 0.25, step: 0.01 }),
|
||||
segmentBoundaryWidth: PD.Numeric(0.05, { min: 0.01, max: 0.25, step: 0.01 }),
|
||||
};
|
||||
type NtCTubeMeshParams = typeof NtCTubeMeshParams;
|
||||
|
||||
type QualityOptions = Exclude<VisualQuality, 'auto' | 'custom'>;
|
||||
const LinearSegmentCount: Record<QualityOptions, number> = {
|
||||
highest: 6,
|
||||
higher: 6,
|
||||
high: 4,
|
||||
medium: 4,
|
||||
low: 3,
|
||||
lower: 3,
|
||||
lowest: 2,
|
||||
};
|
||||
const RadialSegmentCount: Record<QualityOptions, number> = {
|
||||
highest: 32,
|
||||
higher: 26,
|
||||
high: 22,
|
||||
medium: 18,
|
||||
low: 14,
|
||||
lower: 10,
|
||||
lowest: 6,
|
||||
};
|
||||
|
||||
const _curvePoint = Vec3();
|
||||
const _tanA = Vec3();
|
||||
const _tanB = Vec3();
|
||||
const _firstTangentVec = Vec3();
|
||||
const _lastTangentVec = Vec3();
|
||||
const _firstNormalVec = Vec3();
|
||||
const _lastNormalVec = Vec3();
|
||||
|
||||
const _tmpNormal = Vec3();
|
||||
const _tangentVec = Vec3();
|
||||
const _normalVec = Vec3();
|
||||
const _binormalVec = Vec3();
|
||||
const _prevNormal = Vec3();
|
||||
const _nextNormal = Vec3();
|
||||
|
||||
function interpolatePointsAndTangents(state: CurveSegmentState, p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, tRange: number[]) {
|
||||
const { curvePoints, tangentVectors, linearSegments } = state;
|
||||
const tension = 0.5;
|
||||
const r = tRange[1] - tRange[0];
|
||||
|
||||
for (let j = 0; j <= linearSegments; ++j) {
|
||||
const t = j * r / linearSegments + tRange[0];
|
||||
|
||||
v3spline(_curvePoint, p0, p1, p2, p3, t, tension);
|
||||
v3spline(_tanA, p0, p1, p2, p3, t - 0.01, tension);
|
||||
v3spline(_tanB, p0, p1, p2, p3, t + 0.01, tension);
|
||||
|
||||
v3toArray(_curvePoint, curvePoints, j * 3);
|
||||
v3normalize(_tangentVec, v3sub(_tangentVec, _tanA, _tanB));
|
||||
v3toArray(_tangentVec, tangentVectors, j * 3);
|
||||
}
|
||||
}
|
||||
|
||||
function interpolateNormals(state: CurveSegmentState, firstDirection: Vec3, lastDirection: Vec3) {
|
||||
const { curvePoints, tangentVectors, normalVectors, binormalVectors } = state;
|
||||
|
||||
const n = curvePoints.length / 3;
|
||||
|
||||
v3fromArray(_firstTangentVec, tangentVectors, 0);
|
||||
v3fromArray(_lastTangentVec, tangentVectors, (n - 1) * 3);
|
||||
|
||||
v3orthogonalize(_firstNormalVec, _firstTangentVec, firstDirection);
|
||||
v3orthogonalize(_lastNormalVec, _lastTangentVec, lastDirection);
|
||||
v3matchDirection(_lastNormalVec, _lastNormalVec, _firstNormalVec);
|
||||
|
||||
v3copy(_prevNormal, _firstNormalVec);
|
||||
|
||||
const n1 = n - 1;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const j = smoothstep(0, n1, i) * n1;
|
||||
const t = i === 0 ? 0 : 1 / (n - j);
|
||||
|
||||
v3fromArray(_tangentVec, tangentVectors, i * 3);
|
||||
|
||||
v3orthogonalize(_normalVec, _tangentVec, v3slerp(_tmpNormal, _prevNormal, _lastNormalVec, t));
|
||||
v3toArray(_normalVec, normalVectors, i * 3);
|
||||
|
||||
v3copy(_prevNormal, _normalVec);
|
||||
|
||||
v3normalize(_binormalVec, v3cross(_binormalVec, _tangentVec, _normalVec));
|
||||
v3toArray(_binormalVec, binormalVectors, i * 3);
|
||||
}
|
||||
|
||||
for (let i = 1; i < n1; ++i) {
|
||||
v3fromArray(_prevNormal, normalVectors, (i - 1) * 3);
|
||||
v3fromArray(_normalVec, normalVectors, i * 3);
|
||||
v3fromArray(_nextNormal, normalVectors, (i + 1) * 3);
|
||||
|
||||
v3scale(_normalVec, v3add(_normalVec, _prevNormal, v3add(_normalVec, _nextNormal, _normalVec)), 1 / 3);
|
||||
v3toArray(_normalVec, normalVectors, i * 3);
|
||||
|
||||
v3fromArray(_tangentVec, tangentVectors, i * 3);
|
||||
v3normalize(_binormalVec, v3cross(_binormalVec, _tangentVec, _normalVec));
|
||||
v3toArray(_binormalVec, binormalVectors, i * 3);
|
||||
}
|
||||
}
|
||||
|
||||
function interpolate(state: CurveSegmentState, p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, firstDir: Vec3, lastDir: Vec3, tRange = [0, 1]) {
|
||||
interpolatePointsAndTangents(state, p0, p1, p2, p3, tRange);
|
||||
interpolateNormals(state, firstDir, lastDir);
|
||||
}
|
||||
|
||||
function createNtCTubeSegmentsIterator(structureGroup: StructureGroup): LocationIterator {
|
||||
const { structure, group } = structureGroup;
|
||||
const instanceCount = group.units.length;
|
||||
|
||||
const data = NtCTubeProvider.get(structure.model)?.value?.data;
|
||||
if (!data) return LocationIterator(0, 1, 1, () => NullLocation);
|
||||
|
||||
const numBlocks = data.data.steps.length * 4;
|
||||
|
||||
const getLocation = (groupId: number, instanceId: number) => {
|
||||
if (groupId > numBlocks) return NullLocation;
|
||||
const stepIdx = Math.floor(groupId / 4);
|
||||
const step = data.data.steps[stepIdx];
|
||||
const r = groupId % 4;
|
||||
const kind =
|
||||
r === 0 ? 'upper' :
|
||||
r === 1 ? 'lower' :
|
||||
r === 2 ? 'residue-boundary' : 'segment-boundary';
|
||||
|
||||
return NTT.Location({ step, kind });
|
||||
};
|
||||
return LocationIterator(totalMeshGroupsCount(data.data.steps) + 1, instanceCount, 1, getLocation);
|
||||
}
|
||||
|
||||
function segmentCount(structure: Structure, props: PD.Values<NtCTubeMeshParams>): { linear: number, radial: number } {
|
||||
const quality = props.quality;
|
||||
|
||||
if (quality === 'custom')
|
||||
return { linear: props.linearSegments, radial: props.radialSegments };
|
||||
else if (quality === 'auto') {
|
||||
const autoQuality = getStructureQuality(structure) as QualityOptions;
|
||||
return { linear: LinearSegmentCount[autoQuality], radial: RadialSegmentCount[autoQuality] };
|
||||
} else
|
||||
return { linear: LinearSegmentCount[quality], radial: RadialSegmentCount[quality] };
|
||||
}
|
||||
|
||||
function stepBoundingSphere(step: DnatcoTypes.Step, struLoci: StructureElement.Loci): Sphere3D | undefined {
|
||||
const one = DnatcoUtil.residueToLoci(step.auth_asym_id_1, step.auth_seq_id_1, step.label_alt_id_1, step.PDB_ins_code_1, struLoci, 'auth');
|
||||
const two = DnatcoUtil.residueToLoci(step.auth_asym_id_2, step.auth_seq_id_2, step.label_alt_id_2, step.PDB_ins_code_2, struLoci, 'auth');
|
||||
|
||||
if (StructureElement.Loci.is(one) && StructureElement.Loci.is(two)) {
|
||||
const union = structureUnion(struLoci.structure, [StructureElement.Loci.toStructure(one), StructureElement.Loci.toStructure(two)]);
|
||||
return union.boundary.sphere;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
function totalMeshGroupsCount(steps: DnatcoTypes.Step[]) {
|
||||
// Each segment has two blocks, Residue Boundary marker and a Segment Boundary marker
|
||||
return steps.length * 4 - 1; // Subtract one because the last Segment Boundary marker is not drawn
|
||||
}
|
||||
|
||||
function createNtCTubeMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<NtCTubeMeshParams>, mesh?: Mesh) {
|
||||
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
|
||||
|
||||
const prop = NtCTubeProvider.get(structure.model).value;
|
||||
if (prop === undefined || prop.data === undefined) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { data } = prop.data;
|
||||
if (data.steps.length === 0) return Mesh.createEmpty(mesh);
|
||||
|
||||
const MarkerLinearSegmentCount = 2;
|
||||
const segCount = segmentCount(structure, props);
|
||||
const vertexCount = Math.floor((segCount.linear * 4 * data.steps.length / structure.model.atomicHierarchy.chains._rowCount) * segCount.radial);
|
||||
const chunkSize = Math.floor(vertexCount / 3);
|
||||
const diameter = 1.0 * theme.size.props.value;
|
||||
|
||||
const mb = MeshBuilder.createState(vertexCount, chunkSize, mesh);
|
||||
|
||||
const state = createCurveSegmentState(segCount.linear);
|
||||
const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state;
|
||||
for (let idx = 0; idx <= segCount.linear; idx++) {
|
||||
widthValues[idx] = diameter;
|
||||
heightValues[idx] = diameter;
|
||||
}
|
||||
const [normals, binormals] = [binormalVectors, normalVectors]; // Needed so that the tube is not drawn from inside out
|
||||
|
||||
const markerState = createCurveSegmentState(MarkerLinearSegmentCount);
|
||||
const { curvePoints: mCurvePoints, normalVectors: mNormalVectors, binormalVectors: mBinormalVectors, widthValues: mWidthValues, heightValues: mHeightValues } = markerState;
|
||||
for (let idx = 0; idx <= MarkerLinearSegmentCount; idx++) {
|
||||
mWidthValues[idx] = diameter;
|
||||
mHeightValues[idx] = diameter;
|
||||
}
|
||||
const [mNormals, mBinormals] = [mBinormalVectors, mNormalVectors];
|
||||
|
||||
const firstDir = Vec3();
|
||||
const lastDir = Vec3();
|
||||
const markerDir = Vec3();
|
||||
|
||||
const residueMarkerWidth = props.residueMarkerWidth / 2;
|
||||
const it = new NtCTubeSegmentsIterator(structure, unit);
|
||||
while (it.hasNext) {
|
||||
const segment = it.move();
|
||||
if (!segment)
|
||||
continue;
|
||||
|
||||
const { p_1, p0, p1, p2, p3, p4, pP } = segment;
|
||||
const FirstBlockId = segment.stepIdx * 4;
|
||||
const SecondBlockId = FirstBlockId + 1;
|
||||
const ResidueMarkerId = FirstBlockId + 2;
|
||||
const SegmentBoundaryMarkerId = FirstBlockId + 3;
|
||||
|
||||
const { rmShift, rmPos } = calcResidueMarkerShift(p2, p3, pP);
|
||||
|
||||
if (segment.firstInChain) {
|
||||
v3normalize(firstDir, v3sub(firstDir, p2, p1));
|
||||
v3normalize(lastDir, v3sub(lastDir, rmPos, p2));
|
||||
} else {
|
||||
v3copy(firstDir, lastDir);
|
||||
v3normalize(lastDir, v3sub(lastDir, rmPos, p2));
|
||||
}
|
||||
|
||||
// C5' -> O3' block
|
||||
interpolate(state, p0, p1, p2, p3, firstDir, lastDir);
|
||||
mb.currentGroup = FirstBlockId;
|
||||
addTube(mb, curvePoints, normals, binormals, segCount.linear, segCount.radial, widthValues, heightValues, segment.firstInChain || segment.followsGap, false, 'rounded');
|
||||
|
||||
// O3' -> C5' block
|
||||
v3copy(firstDir, lastDir);
|
||||
v3normalize(markerDir, v3sub(markerDir, p3, rmPos));
|
||||
v3normalize(lastDir, v3sub(lastDir, p4, p3));
|
||||
|
||||
// From O3' to the residue marker
|
||||
interpolate(state, p1, p2, p3, p4, firstDir, markerDir, [0, rmShift - residueMarkerWidth]);
|
||||
mb.currentGroup = SecondBlockId;
|
||||
addTube(mb, curvePoints, normals, binormals, segCount.linear, segCount.radial, widthValues, heightValues, false, false, 'rounded');
|
||||
|
||||
// Residue marker
|
||||
interpolate(markerState, p1, p2, p3, p4, markerDir, markerDir, [rmShift - residueMarkerWidth, rmShift + residueMarkerWidth]);
|
||||
mb.currentGroup = ResidueMarkerId;
|
||||
addTube(mb, mCurvePoints, mNormals, mBinormals, MarkerLinearSegmentCount, segCount.radial, mWidthValues, mHeightValues, false, false, 'rounded');
|
||||
|
||||
if (segment.capEnd) {
|
||||
// From the residue marker to C5' of the end
|
||||
interpolate(state, p1, p2, p3, p4, markerDir, lastDir, [rmShift + residueMarkerWidth, 1]);
|
||||
mb.currentGroup = SecondBlockId;
|
||||
addTube(mb, curvePoints, normals, binormals, segCount.linear, segCount.radial, widthValues, heightValues, false, true, 'rounded');
|
||||
} else {
|
||||
// From the residue marker to C5' of the step boundary marker
|
||||
interpolate(state, p1, p2, p3, p4, markerDir, lastDir, [rmShift + residueMarkerWidth, 1 - props.segmentBoundaryWidth]);
|
||||
mb.currentGroup = SecondBlockId;
|
||||
addTube(mb, curvePoints, normals, binormals, segCount.linear, segCount.radial, widthValues, heightValues, false, false, 'rounded');
|
||||
|
||||
// Step boundary marker
|
||||
interpolate(markerState, p1, p2, p3, p4, lastDir, lastDir, [1 - props.segmentBoundaryWidth, 1]);
|
||||
mb.currentGroup = SegmentBoundaryMarkerId;
|
||||
addTube(mb, mCurvePoints, mNormals, mBinormals, MarkerLinearSegmentCount, segCount.radial, mWidthValues, mHeightValues, false, false, 'rounded');
|
||||
}
|
||||
|
||||
if (segment.followsGap) {
|
||||
const cylinderProps: CylinderProps = {
|
||||
radiusTop: diameter / 2, radiusBottom: diameter / 2, topCap: true, bottomCap: true, radialSegments: segCount.radial,
|
||||
};
|
||||
mb.currentGroup = FirstBlockId;
|
||||
addFixedCountDashedCylinder(mb, p_1, p1, 1, 2 * segCount.linear, cylinderProps);
|
||||
}
|
||||
}
|
||||
|
||||
const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1.05);
|
||||
|
||||
const m = MeshBuilder.getMesh(mb);
|
||||
m.setBoundingSphere(boundingSphere);
|
||||
return m;
|
||||
}
|
||||
|
||||
const _rmvCO = Vec3();
|
||||
const _rmvPO = Vec3();
|
||||
const _rmPos = Vec3();
|
||||
const _HalfPi = Math.PI / 2;
|
||||
function calcResidueMarkerShift(pO: Vec3, pC: Vec3, pP: Vec3): { rmShift: number, rmPos: Vec3 } {
|
||||
v3sub(_rmvCO, pC, pO);
|
||||
v3sub(_rmvPO, pP, pO);
|
||||
|
||||
// Project position of P atom on the O3' -> C5' vector
|
||||
const beta = Vec3.angle(_rmvPO, _rmvCO);
|
||||
const alpha = _HalfPi - Math.abs(beta);
|
||||
const lengthMO = Math.cos(alpha) * Vec3.magnitude(_rmvPO);
|
||||
const shift = lengthMO / Vec3.magnitude(_rmvCO);
|
||||
|
||||
v3scale(_rmvCO, _rmvCO, shift);
|
||||
v3add(_rmPos, _rmvCO, pO);
|
||||
|
||||
return { rmShift: shift, rmPos: _rmPos };
|
||||
}
|
||||
|
||||
function getNtCTubeSegmentLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
|
||||
const { groupId, objectId, instanceId } = pickingId;
|
||||
if (objectId !== id) return EmptyLoci;
|
||||
|
||||
const { structure } = structureGroup;
|
||||
|
||||
const unit = structureGroup.group.units[instanceId];
|
||||
if (!Unit.isAtomic(unit)) return EmptyLoci;
|
||||
|
||||
const data = NtCTubeProvider.get(structure.model)?.value?.data ?? undefined;
|
||||
if (!data) return EmptyLoci;
|
||||
|
||||
const MeshGroupsCount = totalMeshGroupsCount(data.data.steps);
|
||||
if (groupId > MeshGroupsCount) return EmptyLoci;
|
||||
|
||||
const stepIdx = Math.floor(groupId / 4);
|
||||
const bs = stepBoundingSphere(data.data.steps[stepIdx], Structure.toStructureElementLoci(structure));
|
||||
|
||||
/*
|
||||
* NOTE 1) Each step is drawn with 4 mesh groups. We need to divide/multiply by 4 to convert between steps and mesh groups.
|
||||
* NOTE 2) Molstar will create a mesh only for the asymmetric unit. When the entire biological assembly
|
||||
* is displayed, Molstar just copies and transforms the mesh. This means that even though the mesh
|
||||
* might be displayed multiple times, groupIds of the individual blocks in the mesh will be the same.
|
||||
* If there are multiple copies of a mesh, Molstar needs to be able to tell which block belongs to which copy of the mesh.
|
||||
* To do that, Molstar adds an offset to groupIds of the copied meshes. Offset is calculated as follows:
|
||||
*
|
||||
* offset = NumberOfBlocks * UnitIndex
|
||||
*
|
||||
* "NumberOfBlocks" is the number of valid Location objects got from LocationIterator *or* the greatest groupId set by
|
||||
* the mesh generator - whichever is smaller.
|
||||
*
|
||||
* UnitIndex is the index of the Unit the mesh belongs to, starting from 0. (See "unitMap" in the Structure object).
|
||||
* We can also get this index from the value "instanceId" of the "pickingId" object.
|
||||
*
|
||||
* If this offset is not applied, picking a piece of one of the copied meshes would actually pick that piece in the original mesh.
|
||||
* This is particularly apparent with highlighting - hovering over items in a copied mesh incorrectly highlights those items in the source mesh.
|
||||
*
|
||||
* Molstar can take advantage of the fact that ElementLoci has a reference to the Unit object attached to it. Since we cannot attach ElementLoci
|
||||
* to a step, we need to calculate the offseted groupId here and pass it as part of the DataLoci.
|
||||
*/
|
||||
const offsetGroupId = stepIdx * 4 + (MeshGroupsCount + 1) * instanceId;
|
||||
return NTT.Loci(data.data.steps, [stepIdx], [offsetGroupId], bs);
|
||||
}
|
||||
|
||||
function eachNtCTubeSegment(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
|
||||
if (NTT.isLoci(loci)) {
|
||||
const offsetGroupId = loci.elements[0];
|
||||
return apply(Interval.ofBounds(offsetGroupId, offsetGroupId + 4));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function NtCTubeVisual(materialId: number): UnitsVisual<NtCTubeMeshParams> {
|
||||
return UnitsMeshVisual<NtCTubeMeshParams>({
|
||||
defaultProps: PD.getDefaultValues(NtCTubeMeshParams),
|
||||
createGeometry: createNtCTubeMesh,
|
||||
createLocationIterator: createNtCTubeSegmentsIterator,
|
||||
getLoci: getNtCTubeSegmentLoci,
|
||||
eachLocation: eachNtCTubeSegment,
|
||||
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<NtCTubeMeshParams>, currentProps: PD.Values<NtCTubeMeshParams>) => {
|
||||
state.createGeometry = (
|
||||
newProps.quality !== currentProps.quality ||
|
||||
newProps.residueMarkerWidth !== currentProps.residueMarkerWidth ||
|
||||
newProps.segmentBoundaryWidth !== currentProps.segmentBoundaryWidth ||
|
||||
newProps.doubleSided !== currentProps.doubleSided ||
|
||||
newProps.alpha !== currentProps.alpha ||
|
||||
newProps.linearSegments !== currentProps.linearSegments ||
|
||||
newProps.radialSegments !== currentProps.radialSegments
|
||||
);
|
||||
}
|
||||
}, materialId);
|
||||
|
||||
}
|
||||
const NtCTubeVisuals = {
|
||||
'ntc-tube-symbol': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, NtCTubeMeshParams>) => UnitsRepresentation('NtC Tube Mesh', ctx, getParams, NtCTubeVisual),
|
||||
};
|
||||
|
||||
export const NtCTubeParams = {
|
||||
...NtCTubeMeshParams
|
||||
};
|
||||
export type NtCTubeParams = typeof NtCTubeParams;
|
||||
export function getNtCTubeParams(ctx: ThemeRegistryContext, structure: Structure) {
|
||||
return PD.clone(NtCTubeParams);
|
||||
}
|
||||
|
||||
export type NtCTubeRepresentation = StructureRepresentation<NtCTubeParams>;
|
||||
export function NtCTubeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, NtCTubeParams>): NtCTubeRepresentation {
|
||||
return Representation.createMulti('NtC Tube', ctx, getParams, StructureRepresentationStateBuilder, NtCTubeVisuals as unknown as Representation.Def<Structure, NtCTubeParams>);
|
||||
}
|
||||
|
||||
export const NtCTubeRepresentationProvider = StructureRepresentationProvider({
|
||||
name: 'ntc-tube',
|
||||
label: 'NtC Tube',
|
||||
description: 'Displays schematic representation of NtC conformers',
|
||||
factory: NtCTubeRepresentation,
|
||||
getParams: getNtCTubeParams,
|
||||
defaultValues: PD.getDefaultValues(NtCTubeParams),
|
||||
defaultColorTheme: { name: 'ntc-tube' },
|
||||
defaultSizeTheme: { name: 'uniform', props: { value: 2.0 } },
|
||||
isApplicable: (structure: Structure) => structure.models.every(m => Dnatco.isApplicable(m)),
|
||||
ensureCustomProperties: {
|
||||
attach: async (ctx: CustomProperty.Context, structure: Structure) => structure.models.forEach(m => NtCTubeProvider.attach(ctx, m, void 0, true)),
|
||||
detach: (data) => data.models.forEach(m => NtCTubeProvider.ref(m, false)),
|
||||
},
|
||||
});
|
||||
51
src/extensions/dnatco/ntc-tube/types.ts
Normal file
51
src/extensions/dnatco/ntc-tube/types.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { NtCTubeSegmentLabel } from './behavior';
|
||||
import { DnatcoTypes } from '../types';
|
||||
import { Sphere3D } from '../../../mol-math/geometry/primitives/sphere3d';
|
||||
import { DataLocation } from '../../../mol-model/location';
|
||||
import { DataLoci } from '../../../mol-model/loci';
|
||||
|
||||
export namespace NtCTubeTypes {
|
||||
const DataTag = 'dnatco-tube-segment-data';
|
||||
const DummyTag = 'dnatco-tube-dummy';
|
||||
|
||||
export type Data = {
|
||||
data: DnatcoTypes.Steps,
|
||||
}
|
||||
|
||||
export type TubeBlock = {
|
||||
step: DnatcoTypes.Step,
|
||||
kind: 'upper' | 'lower' | 'residue-boundary' | 'segment-boundary';
|
||||
}
|
||||
|
||||
export interface Location extends DataLocation<TubeBlock> {}
|
||||
|
||||
export function Location(payload: TubeBlock) {
|
||||
return DataLocation(DataTag, payload, {});
|
||||
}
|
||||
|
||||
export function isLocation(x: any): x is Location {
|
||||
return !!x && x.kind === 'data-location' && x.tag === DataTag;
|
||||
}
|
||||
|
||||
export interface Loci extends DataLoci<DnatcoTypes.Step[], number> {}
|
||||
export interface DummyLoci extends DataLoci<{}, number> {}
|
||||
|
||||
export function Loci(data: DnatcoTypes.Step[], stepIndices: number[], elements: number[], boundingSphere?: Sphere3D): Loci {
|
||||
return DataLoci(DataTag, data, elements, boundingSphere ? () => boundingSphere : undefined, () => stepIndices[0] !== undefined ? NtCTubeSegmentLabel(data[stepIndices[0]]) : '');
|
||||
}
|
||||
|
||||
export function DummyLoci(): DummyLoci {
|
||||
return DataLoci(DummyTag, {}, [], undefined, () => '');
|
||||
}
|
||||
|
||||
export function isLoci(x: any): x is Loci {
|
||||
return !!x && x.kind === 'data-loci' && x.tag === DataTag;
|
||||
}
|
||||
}
|
||||
191
src/extensions/dnatco/ntc-tube/util.ts
Normal file
191
src/extensions/dnatco/ntc-tube/util.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { NtCTubeTypes as NTT } from './types';
|
||||
import { NtCTubeProvider } from './property';
|
||||
import { DnatcoUtil } from '../util';
|
||||
import { Segmentation, SortedArray } from '../../../mol-data/int';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
|
||||
|
||||
function getAtomPosition(vec: Vec3, loc: StructureElement.Location, residue: DnatcoUtil.Residue, names: string[], altId: string, insCode: string) {
|
||||
const eI = DnatcoUtil.getAtomIndex(loc, residue, names, altId, insCode);
|
||||
if (eI !== -1)
|
||||
loc.unit.conformation.invariantPosition(eI, vec);
|
||||
else {
|
||||
vec[0] = 0; vec[1] = 0; vec[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const p_1 = Vec3();
|
||||
const p0 = Vec3();
|
||||
const p1 = Vec3();
|
||||
const p2 = Vec3();
|
||||
const p3 = Vec3();
|
||||
const p4 = Vec3();
|
||||
const pP = Vec3();
|
||||
|
||||
function getPoints(
|
||||
loc: StructureElement.Location,
|
||||
r0: DnatcoUtil.Residue | undefined, r1: DnatcoUtil.Residue, r2: DnatcoUtil.Residue,
|
||||
altId0: string, altId1: string, altId2: string,
|
||||
insCode0: string, insCode1: string, insCode2: string,
|
||||
) {
|
||||
if (r0) getAtomPosition(p_1, loc, r0, ['C5\'', 'C5*'], altId0, insCode0);
|
||||
r0 ? getAtomPosition(p0, loc, r0, ['O3\'', 'O3*'], altId0, insCode0) : getAtomPosition(p0, loc, r1, ['O5\'', 'O5*'], altId1, insCode1);
|
||||
getAtomPosition(p1, loc, r1, ['C5\'', 'C5*'], altId1, insCode1);
|
||||
getAtomPosition(p2, loc, r1, ['O3\'', 'O3*'], altId1, insCode1);
|
||||
getAtomPosition(p3, loc, r2, ['C5\'', 'C5*'], altId2, insCode2);
|
||||
getAtomPosition(p4, loc, r2, ['O3\'', 'O3*'], altId2, insCode2);
|
||||
getAtomPosition(pP, loc, r2, ['P'], altId2, insCode2);
|
||||
|
||||
return { p_1, p0, p1, p2, p3, p4, pP };
|
||||
}
|
||||
|
||||
function hasGapElements(r: DnatcoUtil.Residue, unit: Unit) {
|
||||
for (let xI = r.start; xI < r.end; xI++) {
|
||||
const eI = unit.elements[xI];
|
||||
if (SortedArray.has(unit.gapElements, eI)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export type NtCTubeSegment = {
|
||||
p_1: Vec3,
|
||||
p0: Vec3,
|
||||
p1: Vec3,
|
||||
p2: Vec3,
|
||||
p3: Vec3,
|
||||
p4: Vec3,
|
||||
pP: Vec3,
|
||||
stepIdx: number,
|
||||
followsGap: boolean,
|
||||
firstInChain: boolean,
|
||||
capEnd: boolean,
|
||||
}
|
||||
|
||||
export class NtCTubeSegmentsIterator {
|
||||
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
|
||||
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
|
||||
|
||||
/* Second residue of the previous step, may be undefined
|
||||
* if we are at the beginning of a chain or right after a discontinuity */
|
||||
private residuePrev?: DnatcoUtil.Residue;
|
||||
/* First residue of the current step */
|
||||
private residueOne?: DnatcoUtil.Residue;
|
||||
/* Second residue of the current step */
|
||||
private residueTwo: DnatcoUtil.Residue;
|
||||
/* First residue of the next step, may be undefined
|
||||
* if we are at the end of a chain.
|
||||
* Undefined value indicates that the iterator has reached the end.*/
|
||||
private residueNext?: DnatcoUtil.Residue;
|
||||
|
||||
private data?: NTT.Data;
|
||||
private altIdOne = '';
|
||||
private insCodeOne = '';
|
||||
private loc: StructureElement.Location;
|
||||
|
||||
private moveStep() {
|
||||
if (!this.residueNext)
|
||||
return void 0;
|
||||
|
||||
/* Assume discontinuity of the ResidueIndex of the residue that would become residue one (= first residue of the corresponding step)
|
||||
* does not equal to ResidueIndex of what would be residue two (= second residue of the corresponding step). */
|
||||
if (this.residueTwo.index + 1 === this.residueNext.index) {
|
||||
this.residuePrev = DnatcoUtil.copyResidue(this.residueOne);
|
||||
this.residueOne = DnatcoUtil.copyResidue(this.residueTwo);
|
||||
this.residueTwo = DnatcoUtil.copyResidue(this.residueNext)!;
|
||||
this.residueNext = this.residueIt.hasNext ? DnatcoUtil.copyResidue(this.residueIt.move())! : void 0;
|
||||
} else {
|
||||
if (!this.residueIt.hasNext) {
|
||||
this.residueNext = void 0;
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// There is discontinuity, act as if we were at the beginning of a chain
|
||||
this.residuePrev = void 0;
|
||||
this.residueOne = DnatcoUtil.copyResidue(this.residueNext);
|
||||
this.residueTwo = DnatcoUtil.copyResidue(this.residueIt.move())!;
|
||||
this.residueNext = this.residueIt.hasNext ? DnatcoUtil.copyResidue(this.residueIt.move())! : void 0;
|
||||
}
|
||||
|
||||
return this.toSegment(this.residuePrev, this.residueOne!, this.residueTwo, this.residueNext);
|
||||
}
|
||||
|
||||
private prime() {
|
||||
if (this.residueIt.hasNext)
|
||||
this.residueTwo = DnatcoUtil.copyResidue(this.residueIt.move())!;
|
||||
if (this.residueIt.hasNext)
|
||||
this.residueNext = this.residueIt.move();
|
||||
}
|
||||
|
||||
private toSegment(r0: DnatcoUtil.Residue | undefined, r1: DnatcoUtil.Residue, r2: DnatcoUtil.Residue, r3: DnatcoUtil.Residue | undefined): NtCTubeSegment | undefined {
|
||||
const indices = DnatcoUtil.getStepIndices(this.data!.data, this.loc, r1);
|
||||
if (indices.length === 0)
|
||||
return void 0;
|
||||
|
||||
const stepIdx = indices[0];
|
||||
const step = this.data!.data.steps[stepIdx];
|
||||
|
||||
const altIdPrev = this.altIdOne;
|
||||
const insCodePrev = this.insCodeOne;
|
||||
this.altIdOne = step.label_alt_id_1;
|
||||
this.insCodeOne = step.PDB_ins_code_1;
|
||||
const altIdTwo = step.label_alt_id_2;
|
||||
const insCodeTwo = step.PDB_ins_code_2;
|
||||
const followsGap = !!r0 && hasGapElements(r0, this.loc.unit) && hasGapElements(r1, this.loc.unit);
|
||||
const precedesDiscontinuity = r3 ? r3.index !== r2.index + 1 : false;
|
||||
|
||||
return {
|
||||
...getPoints(this.loc, r0, r1, r2, altIdPrev, this.altIdOne, altIdTwo, insCodePrev, this.insCodeOne, insCodeTwo),
|
||||
stepIdx,
|
||||
followsGap,
|
||||
firstInChain: !r0,
|
||||
capEnd: !this.residueNext || precedesDiscontinuity || hasGapElements(r2, this.loc.unit),
|
||||
};
|
||||
}
|
||||
|
||||
constructor(structure: Structure, unit: Unit.Atomic) {
|
||||
this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
|
||||
this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
|
||||
|
||||
const prop = NtCTubeProvider.get(unit.model).value;
|
||||
this.data = prop?.data;
|
||||
|
||||
if (this.chainIt.hasNext) {
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
this.prime();
|
||||
}
|
||||
|
||||
this.loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
|
||||
}
|
||||
|
||||
get hasNext() {
|
||||
if (!this.data)
|
||||
return false;
|
||||
return !!this.residueNext
|
||||
? true
|
||||
: this.chainIt.hasNext;
|
||||
}
|
||||
|
||||
move() {
|
||||
if (!!this.residueNext) {
|
||||
return this.moveStep();
|
||||
} else {
|
||||
this.residuePrev = void 0; // Assume discontinuity when we switch chains
|
||||
this.residueNext = void 0;
|
||||
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
this.prime();
|
||||
|
||||
return this.moveStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/extensions/dnatco/property.ts
Normal file
172
src/extensions/dnatco/property.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { DnatcoTypes } from './types';
|
||||
import { Column, Table } from '../../mol-data/db';
|
||||
import { toTable } from '../../mol-io/reader/cif/schema';
|
||||
import { Model } from '../../mol-model/structure';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
|
||||
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
|
||||
export type DnatcoSteps = PropertyWrapper<DnatcoTypes.Steps | undefined>;
|
||||
|
||||
export const DnatcoParams = {};
|
||||
export type DnatcoParams = typeof DnatcoParams;
|
||||
export type DnatcoProps = PD.Values<DnatcoParams>;
|
||||
|
||||
export namespace Dnatco {
|
||||
export const Schema = {
|
||||
ndb_struct_ntc_step: {
|
||||
id: Column.Schema.int,
|
||||
name: Column.Schema.str,
|
||||
PDB_model_number: Column.Schema.int,
|
||||
label_entity_id_1: Column.Schema.int,
|
||||
label_asym_id_1: Column.Schema.str,
|
||||
label_seq_id_1: Column.Schema.int,
|
||||
label_comp_id_1: Column.Schema.str,
|
||||
label_alt_id_1: Column.Schema.str,
|
||||
label_entity_id_2: Column.Schema.int,
|
||||
label_asym_id_2: Column.Schema.str,
|
||||
label_seq_id_2: Column.Schema.int,
|
||||
label_comp_id_2: Column.Schema.str,
|
||||
label_alt_id_2: Column.Schema.str,
|
||||
auth_asym_id_1: Column.Schema.str,
|
||||
auth_seq_id_1: Column.Schema.int,
|
||||
auth_asym_id_2: Column.Schema.str,
|
||||
auth_seq_id_2: Column.Schema.int,
|
||||
PDB_ins_code_1: Column.Schema.str,
|
||||
PDB_ins_code_2: Column.Schema.str,
|
||||
},
|
||||
ndb_struct_ntc_step_summary: {
|
||||
step_id: Column.Schema.int,
|
||||
assigned_CANA: Column.Schema.str,
|
||||
assigned_NtC: Column.Schema.str,
|
||||
confal_score: Column.Schema.int,
|
||||
euclidean_distance_NtC_ideal: Column.Schema.float,
|
||||
cartesian_rmsd_closest_NtC_representative: Column.Schema.float,
|
||||
closest_CANA: Column.Schema.str,
|
||||
closest_NtC: Column.Schema.str,
|
||||
closest_step_golden: Column.Schema.str
|
||||
}
|
||||
};
|
||||
export type Schema = typeof Schema;
|
||||
|
||||
export function getStepsFromCif(
|
||||
model: Model,
|
||||
cifSteps: Table<typeof Dnatco.Schema.ndb_struct_ntc_step>,
|
||||
stepsSummary: StepsSummaryTable
|
||||
): DnatcoTypes.Steps {
|
||||
const steps = new Array<DnatcoTypes.Step>();
|
||||
const mapping = new Array<DnatcoTypes.MappedChains>();
|
||||
|
||||
const {
|
||||
id, PDB_model_number, name,
|
||||
auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
|
||||
auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
|
||||
_rowCount
|
||||
} = cifSteps;
|
||||
|
||||
if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
|
||||
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
const {
|
||||
NtC,
|
||||
confal_score,
|
||||
rmsd
|
||||
} = getSummaryData(id.value(i), i, stepsSummary);
|
||||
const modelNum = PDB_model_number.value(i);
|
||||
const chainId = auth_asym_id_1.value(i);
|
||||
const seqId = auth_seq_id_1.value(i);
|
||||
const modelIdx = modelNum - 1;
|
||||
|
||||
if (mapping.length <= modelIdx || !mapping[modelIdx])
|
||||
mapping[modelIdx] = new Map<string, DnatcoTypes.MappedResidues>();
|
||||
|
||||
const step = {
|
||||
PDB_model_number: modelNum,
|
||||
name: name.value(i),
|
||||
auth_asym_id_1: chainId,
|
||||
auth_seq_id_1: seqId,
|
||||
label_comp_id_1: label_comp_id_1.value(i),
|
||||
label_alt_id_1: label_alt_id_1.value(i),
|
||||
PDB_ins_code_1: PDB_ins_code_1.value(i),
|
||||
auth_asym_id_2: auth_asym_id_2.value(i),
|
||||
auth_seq_id_2: auth_seq_id_2.value(i),
|
||||
label_comp_id_2: label_comp_id_2.value(i),
|
||||
label_alt_id_2: label_alt_id_2.value(i),
|
||||
PDB_ins_code_2: PDB_ins_code_2.value(i),
|
||||
confal_score,
|
||||
NtC,
|
||||
rmsd,
|
||||
};
|
||||
|
||||
steps.push(step);
|
||||
|
||||
const mappedChains = mapping[modelIdx];
|
||||
const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>();
|
||||
const stepsForResidue = residuesOnChain.get(seqId) ?? [];
|
||||
stepsForResidue.push(steps.length - 1);
|
||||
|
||||
residuesOnChain.set(seqId, stepsForResidue);
|
||||
mappedChains.set(chainId, residuesOnChain);
|
||||
mapping[modelIdx] = mappedChains;
|
||||
}
|
||||
|
||||
return { steps, mapping };
|
||||
}
|
||||
|
||||
export async function fromCif(ctx: CustomProperty.Context, model: Model, props: DnatcoProps): Promise<CustomProperty.Data<DnatcoSteps>> {
|
||||
const info = PropertyWrapper.createInfo();
|
||||
const data = getCifData(model);
|
||||
if (data === undefined) return { value: { info, data: undefined } };
|
||||
|
||||
const fromCif = getStepsFromCif(model, data.steps, data.stepsSummary);
|
||||
return { value: { info, data: fromCif } };
|
||||
}
|
||||
|
||||
export function getCifData(model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) throw new Error('Data format must be mmCIF');
|
||||
if (!hasNdbStructNtcCategories(model)) return undefined;
|
||||
return {
|
||||
steps: toTable(Schema.ndb_struct_ntc_step, model.sourceData.data.frame.categories.ndb_struct_ntc_step),
|
||||
stepsSummary: toTable(Schema.ndb_struct_ntc_step_summary, model.sourceData.data.frame.categories.ndb_struct_ntc_step_summary)
|
||||
};
|
||||
}
|
||||
|
||||
function hasNdbStructNtcCategories(model: Model): boolean {
|
||||
if (!MmcifFormat.is(model.sourceData)) return false;
|
||||
const names = (model.sourceData).data.frame.categoryNames;
|
||||
return names.includes('ndb_struct_ntc_step') && names.includes('ndb_struct_ntc_step_summary');
|
||||
}
|
||||
|
||||
export function isApplicable(model?: Model): boolean {
|
||||
return !!model && hasNdbStructNtcCategories(model);
|
||||
}
|
||||
}
|
||||
|
||||
type StepsSummaryTable = Table<typeof Dnatco.Schema.ndb_struct_ntc_step_summary>;
|
||||
|
||||
function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) {
|
||||
const {
|
||||
step_id,
|
||||
confal_score,
|
||||
assigned_NtC,
|
||||
cartesian_rmsd_closest_NtC_representative,
|
||||
} = stepsSummary;
|
||||
|
||||
// Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
|
||||
for (let j = i; j < stepsSummary._rowCount; j++) {
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
// Safety net for cases where the previous assumption is not met
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
throw new Error('Inconsistent mmCIF data');
|
||||
}
|
||||
41
src/extensions/dnatco/types.ts
Normal file
41
src/extensions/dnatco/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
export namespace DnatcoTypes {
|
||||
export const DataTag = 'dnatco-confal-half-step';
|
||||
|
||||
export type Step = {
|
||||
PDB_model_number: number,
|
||||
name: string,
|
||||
auth_asym_id_1: string,
|
||||
auth_seq_id_1: number,
|
||||
label_comp_id_1: string,
|
||||
label_alt_id_1: string,
|
||||
PDB_ins_code_1: string,
|
||||
auth_asym_id_2: string,
|
||||
auth_seq_id_2: number,
|
||||
label_comp_id_2: string,
|
||||
label_alt_id_2: string,
|
||||
PDB_ins_code_2: string,
|
||||
confal_score: number,
|
||||
NtC: string,
|
||||
rmsd: number,
|
||||
}
|
||||
|
||||
export type MappedChains = Map<string, MappedResidues>;
|
||||
export type MappedResidues = Map<number, number[]>;
|
||||
|
||||
export interface Steps {
|
||||
steps: Array<Step>,
|
||||
mapping: MappedChains[],
|
||||
}
|
||||
|
||||
export interface HalfStep {
|
||||
step: Step,
|
||||
isLower: boolean,
|
||||
}
|
||||
}
|
||||
117
src/extensions/dnatco/util.ts
Normal file
117
src/extensions/dnatco/util.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { DnatcoTypes } from './types';
|
||||
import { OrderedSet, Segmentation } from '../../mol-data/int';
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../mol-model/structure';
|
||||
|
||||
const EmptyStepIndices = new Array<number>();
|
||||
|
||||
export namespace DnatcoUtil {
|
||||
export type Residue = Segmentation.Segment<ResidueIndex>;
|
||||
|
||||
export function copyResidue(r?: Residue) {
|
||||
return r ? { index: r.index, start: r.start, end: r.end } : void 0;
|
||||
}
|
||||
|
||||
export function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string, insCode: string): ElementIndex {
|
||||
for (let eI = residue.start; eI < residue.end; eI++) {
|
||||
loc.element = loc.unit.elements[eI];
|
||||
const elName = StructureProperties.atom.label_atom_id(loc);
|
||||
const elAltId = StructureProperties.atom.label_alt_id(loc);
|
||||
const elInsCode = StructureProperties.residue.pdbx_PDB_ins_code(loc);
|
||||
|
||||
if (names.includes(elName) && (elAltId === altId || elAltId.length === 0) && (elInsCode === insCode))
|
||||
return loc.element;
|
||||
}
|
||||
|
||||
return -1 as ElementIndex;
|
||||
}
|
||||
|
||||
export function getStepIndices(data: DnatcoTypes.Steps, loc: StructureElement.Location, r: DnatcoUtil.Residue) {
|
||||
loc.element = loc.unit.elements[r.start];
|
||||
|
||||
const modelIdx = StructureProperties.unit.model_num(loc) - 1;
|
||||
const chainId = StructureProperties.chain.auth_asym_id(loc);
|
||||
const seqId = StructureProperties.residue.auth_seq_id(loc);
|
||||
const insCode = StructureProperties.residue.pdbx_PDB_ins_code(loc);
|
||||
|
||||
const chains = data.mapping[modelIdx];
|
||||
if (!chains) return EmptyStepIndices;
|
||||
const residues = chains.get(chainId);
|
||||
if (!residues) return EmptyStepIndices;
|
||||
const indices = residues.get(seqId);
|
||||
if (!indices) return EmptyStepIndices;
|
||||
|
||||
return insCode !== '' ? indices.filter(idx => data.steps[idx].PDB_ins_code_1 === insCode) : indices;
|
||||
}
|
||||
|
||||
export function residueAltIds(structure: Structure, unit: Unit, residue: Residue) {
|
||||
const altIds = new Array<string>();
|
||||
const loc = StructureElement.Location.create(structure, unit);
|
||||
for (let eI = residue.start; eI < residue.end; eI++) {
|
||||
loc.element = OrderedSet.getAt(unit.elements, eI);
|
||||
const altId = StructureProperties.atom.label_alt_id(loc);
|
||||
if (altId !== '' && !altIds.includes(altId))
|
||||
altIds.push(altId);
|
||||
}
|
||||
|
||||
return altIds;
|
||||
}
|
||||
|
||||
const _loc = StructureElement.Location.create();
|
||||
export function residueToLoci(asymId: string, seqId: number, altId: string | undefined, insCode: string, loci: StructureElement.Loci, source: 'label' | 'auth') {
|
||||
_loc.structure = loci.structure;
|
||||
for (const e of loci.elements) {
|
||||
_loc.unit = e.unit;
|
||||
|
||||
const getAsymId = source === 'label' ? StructureProperties.chain.label_asym_id : StructureProperties.chain.auth_asym_id;
|
||||
const getSeqId = source === 'label' ? StructureProperties.residue.label_seq_id : StructureProperties.residue.auth_seq_id;
|
||||
|
||||
// Walk the entire unit and look for the requested residue
|
||||
const chainIt = Segmentation.transientSegments(e.unit.model.atomicHierarchy.chainAtomSegments, e.unit.elements);
|
||||
const residueIt = Segmentation.transientSegments(e.unit.model.atomicHierarchy.residueAtomSegments, e.unit.elements);
|
||||
|
||||
const elemIndex = (idx: number) => OrderedSet.getAt(e.unit.elements, idx);
|
||||
while (chainIt.hasNext) {
|
||||
const chain = chainIt.move();
|
||||
_loc.element = elemIndex(chain.start);
|
||||
const _asymId = getAsymId(_loc);
|
||||
if (_asymId !== asymId)
|
||||
continue; // Wrong chain, skip it
|
||||
|
||||
residueIt.setSegment(chain);
|
||||
while (residueIt.hasNext) {
|
||||
const residue = residueIt.move();
|
||||
_loc.element = elemIndex(residue.start);
|
||||
|
||||
const _seqId = getSeqId(_loc);
|
||||
if (_seqId === seqId) {
|
||||
const _insCode = StructureProperties.residue.pdbx_PDB_ins_code(_loc);
|
||||
if (_insCode !== insCode)
|
||||
continue;
|
||||
if (altId) {
|
||||
const _altIds = residueAltIds(loci.structure, e.unit, residue);
|
||||
if (!_altIds.includes(altId))
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = residue.start as StructureElement.UnitIndex;
|
||||
const end = residue.end as StructureElement.UnitIndex;
|
||||
return StructureElement.Loci(
|
||||
loci.structure,
|
||||
[{ unit: e.unit, indices: OrderedSet.ofBounds(start, end) }]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EmptyLoci;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
|
||||
|
||||
/**
|
||||
* Represents a set of values to choose from, with a default value. Example:
|
||||
* ```
|
||||
* export const MyChoice = new Choice({ yes: 'I agree', no: 'Nope' }, 'yes');
|
||||
* export type MyChoiceType = Choice.Values<typeof MyChoice>; // 'yes'|'no'
|
||||
* ```
|
||||
*/
|
||||
export class Choice<T extends string, D extends T> {
|
||||
readonly defaultValue: D;
|
||||
readonly options: [T, string][];
|
||||
private readonly nameDict: { [value in T]: string };
|
||||
constructor(opts: { [value in T]: string }, defaultValue: D) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.options = Object.keys(opts).map(k => [k as T, opts[k as T]]);
|
||||
this.nameDict = opts;
|
||||
}
|
||||
PDSelect(defaultValue?: T, info?: PD.Info): PD.Select<T> {
|
||||
return PD.Select<T>(defaultValue ?? this.defaultValue, this.options, info);
|
||||
}
|
||||
prettyName(value: T): string {
|
||||
return this.nameDict[value];
|
||||
}
|
||||
}
|
||||
export namespace Choice {
|
||||
export type Values<T extends Choice<any, any>> = T extends Choice<infer R, any> ? R : any;
|
||||
}
|
||||
@@ -6,31 +6,28 @@
|
||||
|
||||
/** Testing examples for using mesh-extension.ts. */
|
||||
|
||||
import { ParseMeshlistTransformer, MeshShapeTransformer, MeshlistData } from './mesh-extension';
|
||||
import * as MeshUtils from './mesh-utils';
|
||||
import { BACKGROUND_OPACITY, FOREROUND_OPACITY, InitMeshStreaming } from './mesh-streaming/transformers';
|
||||
import { MeshServerInfo } from './mesh-streaming/server-info';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StateObjectRef, StateObjectSelector } from '../../mol-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Download } from '../../mol-plugin-state/transforms/data';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Box3D } from '../../mol-math/geometry';
|
||||
import { ShapeRepresentation3D } from '../../mol-plugin-state/transforms/representation';
|
||||
import { ParamDefinition } from '../../mol-util/param-definition';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { CIF } from '../../mol-io/reader/cif';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StateObjectSelector } from '../../mol-state';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ParamDefinition } from '../../mol-util/param-definition';
|
||||
|
||||
import { createMeshFromUrl } from './mesh-extension';
|
||||
import { MeshServerInfo } from './mesh-streaming/server-info';
|
||||
import { InitMeshStreaming } from './mesh-streaming/transformers';
|
||||
import * as MeshUtils from './mesh-utils';
|
||||
|
||||
|
||||
export const DB_URL = '/db'; // local
|
||||
|
||||
|
||||
export async function runMeshExtensionExamples(plugin: PluginUIContext, db_url: string = DB_URL) {
|
||||
export async function runMeshExtensionExamples(plugin: PluginContext, db_url: string = DB_URL) {
|
||||
console.time('TIME MESH EXAMPLES');
|
||||
// await runIsosurfaceExample(plugin, db_url);
|
||||
// await runMolsurfaceExample(plugin);
|
||||
@@ -47,7 +44,7 @@ export async function runMeshExtensionExamples(plugin: PluginUIContext, db_url:
|
||||
}
|
||||
|
||||
/** Example for downloading multiple separate segments, each containing 1 mesh. */
|
||||
export async function runMeshExample(plugin: PluginUIContext, segments: 'fg' | 'all', db_url: string = DB_URL) {
|
||||
export async function runMeshExample(plugin: PluginContext, segments: 'fg' | 'all', db_url: string = DB_URL) {
|
||||
const detail = 2;
|
||||
const segmentIds = (segments === 'all') ?
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17] // segment-16 has no detail-2
|
||||
@@ -59,7 +56,7 @@ export async function runMeshExample(plugin: PluginUIContext, segments: 'fg' | '
|
||||
}
|
||||
|
||||
/** Example for downloading multiple separate segments, each containing 1 mesh. */
|
||||
export async function runMeshExample2(plugin: PluginUIContext, segments: 'one' | 'few' | 'fg' | 'all') {
|
||||
export async function runMeshExample2(plugin: PluginContext, segments: 'one' | 'few' | 'fg' | 'all') {
|
||||
const detail = 1;
|
||||
const segmentIds = (segments === 'one') ? [15]
|
||||
: (segments === 'few') ? [1, 4, 7, 10, 16]
|
||||
@@ -72,48 +69,13 @@ export async function runMeshExample2(plugin: PluginUIContext, segments: 'one' |
|
||||
}
|
||||
|
||||
/** Example for downloading a single segment containing multiple meshes. */
|
||||
export async function runMultimeshExample(plugin: PluginUIContext, segments: 'fg' | 'all', detailChoice: 'best' | 'worst', db_url: string = DB_URL) {
|
||||
export async function runMultimeshExample(plugin: PluginContext, segments: 'fg' | 'all', detailChoice: 'best' | 'worst', db_url: string = DB_URL) {
|
||||
const urlDetail = (detailChoice === 'best') ? '2' : 'worst';
|
||||
const numDetail = (detailChoice === 'best') ? 2 : 1000;
|
||||
await createMeshFromUrl(plugin, `${db_url}/empiar-10070-multimesh-rounded/segments-${segments}/detail-${urlDetail}`, 0, numDetail, false, undefined);
|
||||
}
|
||||
|
||||
/** Download data and create state tree hierarchy down to visual representation. */
|
||||
export async function createMeshFromUrl(plugin: PluginContext, meshDataUrl: string, segmentId: number, detail: number,
|
||||
collapseTree: boolean, color?: Color, parent?: StateObjectSelector | StateObjectRef, transparentIfBboxAbove?: number,
|
||||
name?: string, ownerId?: string) {
|
||||
|
||||
const update = parent ? plugin.build().to(parent) : plugin.build().toRoot();
|
||||
const rawDataNodeRef = update.apply(Download,
|
||||
{ url: meshDataUrl, isBinary: true, label: `Downloaded Data ${segmentId}` },
|
||||
{ state: { isCollapsed: collapseTree } }
|
||||
).ref;
|
||||
const parsedDataNode = await update.to(rawDataNodeRef)
|
||||
.apply(StateTransforms.Data.ParseCif)
|
||||
.apply(ParseMeshlistTransformer,
|
||||
{ label: undefined, segmentId: segmentId, segmentName: name ?? `Segment ${segmentId}`, detail: detail, ownerId: ownerId },
|
||||
{}
|
||||
)
|
||||
.commit();
|
||||
|
||||
let transparent = false;
|
||||
if (transparentIfBboxAbove !== undefined && parsedDataNode.data) {
|
||||
const bbox = MeshlistData.bbox(parsedDataNode.data) || Box3D.zero();
|
||||
transparent = Box3D.volume(bbox) > transparentIfBboxAbove;
|
||||
}
|
||||
|
||||
await plugin.build().to(parsedDataNode)
|
||||
.apply(MeshShapeTransformer, { color: color },)
|
||||
.apply(ShapeRepresentation3D,
|
||||
{ alpha: transparent ? BACKGROUND_OPACITY : FOREROUND_OPACITY },
|
||||
{ tags: ['mesh-segment-visual', `segment-${segmentId}`] }
|
||||
)
|
||||
.commit();
|
||||
|
||||
return rawDataNodeRef;
|
||||
}
|
||||
|
||||
export async function runMeshStreamingExample(plugin: PluginUIContext, source: MeshServerInfo.MeshSource = 'empiar', entryId: string = 'empiar-10070', serverUrl?: string, parent?: StateObjectSelector) {
|
||||
export async function runMeshStreamingExample(plugin: PluginContext, source: MeshServerInfo.MeshSource = 'empiar', entryId: string = 'empiar-10070', serverUrl?: string, parent?: StateObjectSelector) {
|
||||
const params = ParamDefinition.getDefaultValues(MeshServerInfo.Params);
|
||||
if (serverUrl) params.serverUrl = serverUrl;
|
||||
params.source = source;
|
||||
@@ -122,7 +84,7 @@ export async function runMeshStreamingExample(plugin: PluginUIContext, source: M
|
||||
}
|
||||
|
||||
/** Example for downloading a protein structure and visualizing molecular surface. */
|
||||
export async function runMolsurfaceExample(plugin: PluginUIContext) {
|
||||
export async function runMolsurfaceExample(plugin: PluginContext) {
|
||||
const entryId = 'pdb-7etq';
|
||||
|
||||
// Node "https://www.ebi.ac.uk/pdbe/entry-files/download/7etq.bcif" ("transformer": "ms-plugin.download") -> var data
|
||||
@@ -151,7 +113,7 @@ export async function runMolsurfaceExample(plugin: PluginUIContext) {
|
||||
}
|
||||
|
||||
/** Example for downloading an EMDB density data and visualizing isosurface. */
|
||||
export async function runIsosurfaceExample(plugin: PluginUIContext, db_url: string = DB_URL) {
|
||||
export async function runIsosurfaceExample(plugin: PluginContext, db_url: string = DB_URL) {
|
||||
const entryId = 'emd-1832';
|
||||
const isoLevel = 2.73;
|
||||
|
||||
@@ -203,7 +165,7 @@ export async function runIsosurfaceExample(plugin: PluginUIContext, db_url: stri
|
||||
}
|
||||
|
||||
|
||||
export async function runCifMeshExample(plugin: PluginUIContext, api: string = 'http://localhost:9000/v2',
|
||||
export async function runCifMeshExample(plugin: PluginContext, api: string = 'http://localhost:9000/v2',
|
||||
source: MeshServerInfo.MeshSource = 'empiar', entryId: string = 'empiar-10070',
|
||||
segmentId: number = 1, detail: number = 10,
|
||||
) {
|
||||
@@ -211,8 +173,8 @@ export async function runCifMeshExample(plugin: PluginUIContext, api: string = '
|
||||
getMeshFromBcif(plugin, url);
|
||||
}
|
||||
|
||||
async function getMeshFromBcif(plugin: PluginUIContext, url: string) {
|
||||
const urlAsset = Asset.getUrlAsset(plugin.managers.asset, url); // QUESTION how is urlAsset better than normal `fetch`
|
||||
async function getMeshFromBcif(plugin: PluginContext, url: string) {
|
||||
const urlAsset = Asset.getUrlAsset(plugin.managers.asset, url);
|
||||
const asset = await plugin.runTask(plugin.managers.asset.resolve(urlAsset, 'binary'));
|
||||
const parsed = await plugin.runTask(CIF.parseBinary(asset.data));
|
||||
if (parsed.isError) {
|
||||
|
||||
@@ -15,14 +15,20 @@ import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { ShapeProvider } from '../../mol-model/shape/provider';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransformer } from '../../mol-state';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Download } from '../../mol-plugin-state/transforms/data';
|
||||
import { ShapeRepresentation3D } from '../../mol-plugin-state/transforms/representation';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
|
||||
import { Task } from '../../mol-task';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Material } from '../../mol-util/material';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import * as MeshUtils from './mesh-utils';
|
||||
|
||||
|
||||
export const BACKGROUND_OPACITY = 0.2;
|
||||
export const FOREROUND_OPACITY = 1;
|
||||
|
||||
export const VolsegTransform: StateTransformer.Builder.Root = StateTransformer.builderFactory('volseg');
|
||||
|
||||
|
||||
@@ -106,19 +112,17 @@ export namespace MeshlistData {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Raw Data -> Parsed data
|
||||
|
||||
export class MeshlistStateObject extends PluginStateObject.Create<MeshlistData>({ name: 'Parsed Meshlist', typeClass: 'Object' }) { }
|
||||
// QUESTION: is typeClass just for color, or does do something?
|
||||
|
||||
export const ParseMeshlistTransformer = VolsegTransform({
|
||||
name: 'meshlist-from-string',
|
||||
from: PluginStateObject.Format.Cif,
|
||||
to: MeshlistStateObject,
|
||||
params: {
|
||||
label: PD.Text(MeshlistStateObject.type.name, { isHidden: true }), // QUESTION: Is this the right way to pass a value to apply() without exposing it in GUI?
|
||||
label: PD.Text(MeshlistStateObject.type.name, { isHidden: true }),
|
||||
segmentId: PD.Numeric(1, {}, { isHidden: true }),
|
||||
segmentName: PD.Text('Segment'),
|
||||
detail: PD.Numeric(1, {}, { isHidden: true }),
|
||||
@@ -148,47 +152,32 @@ namespace MeshShapeProvider {
|
||||
return {
|
||||
label: 'Mesh',
|
||||
data: meshlist,
|
||||
params: meshParamDef, // TODO how to pass the real params correctly?
|
||||
params: meshShapeProviderParams,
|
||||
geometryUtils: Mesh.Utils,
|
||||
getShape: (ctx, data: MeshlistData) => MeshlistData.getShape(data, theColor),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Params for MeshShapeTransformer */
|
||||
const meshShapeParamDef = {
|
||||
color: PD.Value<Color | undefined>(undefined), // undefined means random color
|
||||
const meshShapeProviderParams: Mesh.Params = {
|
||||
...Mesh.Params,
|
||||
quality: PD.Select<VisualQuality>('custom', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }), // use 'custom' when wanting to apply doubleSided
|
||||
doubleSided: PD.Boolean(true, BaseGeometry.CustomQualityParamInfo),
|
||||
// set `flatShaded`: true to see the real mesh vertices and triangles
|
||||
transparentBackfaces: PD.Select('on', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory), // 'on' means: show backfaces with correct opacity, even when opacity < 1 (requires doubleSided) ¯\_(ツ)_/¯
|
||||
};
|
||||
|
||||
const meshParamDef: Mesh.Params = {
|
||||
// These are basically original MS.Mesh.Params:
|
||||
// BaseGeometry.Params
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
|
||||
quality: PD.Select<VisualQuality>('custom', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }), // use 'custom' when wanting to apply doubleSided
|
||||
material: Material.getParam(),
|
||||
clip: Mesh.Params.clip, // PD.Group(MS.Clip.Params),
|
||||
instanceGranularity: PD.Boolean(false, { description: 'Use instance granularity for marker, transparency, clipping, overpaint, substance data to save memory.' }),
|
||||
// Mesh.Params
|
||||
doubleSided: PD.Boolean(true, BaseGeometry.CustomQualityParamInfo), // default: false (set true, to show at least something in weird cases)
|
||||
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory), // default: false (set true to see the real mesh vertices and triangles)
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory), // this is like better opacity (angle-dependent), nice
|
||||
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
|
||||
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
// TODO when I change values here, it has effect, but not if I change them in GUI
|
||||
};
|
||||
|
||||
export const MeshShapeTransformer = VolsegTransform({
|
||||
name: 'shape-from-meshlist',
|
||||
display: { name: 'Shape from Meshlist', description: 'Create Shape from Meshlist data' },
|
||||
from: MeshlistStateObject,
|
||||
to: PluginStateObject.Shape.Provider,
|
||||
params: meshShapeParamDef
|
||||
params: {
|
||||
color: PD.Value<Color | undefined>(undefined), // undefined means random color
|
||||
},
|
||||
})({
|
||||
apply({ a, params }) {
|
||||
// you can look for example at ShapeFromPly in mol-plugin-state/tansforms/model.ts as an example
|
||||
const shapeProvider = MeshShapeProvider.fromMeshlistData(a.data, params.color);
|
||||
return new PluginStateObject.Shape.Provider(shapeProvider, { label: PluginStateObject.Shape.Provider.type.name, description: a.description });
|
||||
}
|
||||
@@ -196,32 +185,39 @@ export const MeshShapeTransformer = VolsegTransform({
|
||||
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Shape -> Repr
|
||||
|
||||
// type MeshRepr = MS.PluginStateObject.Representation3DData<MS.ShapeRepresentation<MS.ShapeProvider<any,any,any>, MS.Mesh, MS.Mesh.Params>, any>;
|
||||
|
||||
// export const CustomMeshReprTransformer = VolsegTransform({
|
||||
// name: 'custom-repr',
|
||||
// from: MS.PluginStateObject.Shape.Provider, // later we can change this
|
||||
// to: MS.PluginStateObject.Shape.Representation3D,
|
||||
// })({
|
||||
// apply({ a }, globalCtx) {
|
||||
// const repr: MeshRepr = createRepr(a.data); // TODO implement createRepr
|
||||
// // have a look at MS.StateTransforms.Representation.ShapeRepresentation3D if you want to try implementing yourself
|
||||
// return new MS.PluginStateObject.Shape.Representation3D(repr)
|
||||
// },
|
||||
// })
|
||||
/** Download data and create state tree hierarchy down to visual representation. */
|
||||
export async function createMeshFromUrl(plugin: PluginContext, meshDataUrl: string, segmentId: number, detail: number,
|
||||
collapseTree: boolean, color?: Color, parent?: StateObjectSelector | StateObjectRef, transparentIfBboxAbove?: number,
|
||||
name?: string, ownerId?: string) {
|
||||
|
||||
// export async function createMeshRepr(plugin: MS.PluginContext, data: any) {
|
||||
// await plugin.build()
|
||||
// .toRoot()
|
||||
// .apply(CreateMyShapeTransformer, { data })
|
||||
// .apply(MS.StateTransforms.Representation.ShapeRepresentation3D) // this should work
|
||||
// // or .apply(CustomMeshRepr)
|
||||
// .commit();
|
||||
// }
|
||||
const update = parent ? plugin.build().to(parent) : plugin.build().toRoot();
|
||||
const rawDataNodeRef = update.apply(Download,
|
||||
{ url: meshDataUrl, isBinary: true, label: `Downloaded Data ${segmentId}` },
|
||||
{ state: { isCollapsed: collapseTree } }
|
||||
).ref;
|
||||
const parsedDataNode = await update.to(rawDataNodeRef)
|
||||
.apply(StateTransforms.Data.ParseCif)
|
||||
.apply(ParseMeshlistTransformer,
|
||||
{ label: undefined, segmentId: segmentId, segmentName: name ?? `Segment ${segmentId}`, detail: detail, ownerId: ownerId },
|
||||
{}
|
||||
)
|
||||
.commit();
|
||||
|
||||
// export function createRepr(reprData: MS.ShapeProvider<any,any,any>): MeshRepr {
|
||||
// throw new Error('NotImplemented');
|
||||
// return {} as MeshRepr;
|
||||
// }
|
||||
let transparent = false;
|
||||
if (transparentIfBboxAbove !== undefined && parsedDataNode.data) {
|
||||
const bbox = MeshlistData.bbox(parsedDataNode.data) || Box3D.zero();
|
||||
transparent = Box3D.volume(bbox) > transparentIfBboxAbove;
|
||||
}
|
||||
|
||||
await plugin.build().to(parsedDataNode)
|
||||
.apply(MeshShapeTransformer, { color: color },)
|
||||
.apply(ShapeRepresentation3D,
|
||||
{ alpha: transparent ? BACKGROUND_OPACITY : FOREROUND_OPACITY },
|
||||
{ tags: ['mesh-segment-visual', `segment-${segmentId}`] }
|
||||
)
|
||||
.commit();
|
||||
|
||||
return rawDataNodeRef;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@ import { Color } from '../../../mol-util/color';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
import { Choice } from '../choice';
|
||||
import { Choice } from '../../volumes-and-segmentations/helpers';
|
||||
import { MetadataWrapper } from '../../volumes-and-segmentations/volseg-api/utils';
|
||||
|
||||
import { MeshlistData } from '../mesh-extension';
|
||||
import { Metadata } from '../metadata';
|
||||
import { MeshServerInfo } from './server-info';
|
||||
|
||||
|
||||
@@ -33,8 +34,6 @@ const MAX_DETAIL = 10;
|
||||
const DEFAULT_DETAIL = 7; // TODO decide a reasonable default
|
||||
/** Segments whose bounding box volume is above this value (relative to the overall bounding box) are considered as background segments */
|
||||
export const BACKGROUND_SEGMENT_VOLUME_THRESHOLD = 0.5;
|
||||
// const DEBUG_IGNORED_SEGMENTS = new Set([13, 15]); // TODO remove
|
||||
const DEBUG_IGNORED_SEGMENTS = new Set(); // TODO remove
|
||||
|
||||
|
||||
export class MeshStreaming extends PluginStateObject.CreateBehavior<MeshStreaming.Behavior>({ name: 'Mesh Streaming' }) { }
|
||||
@@ -116,7 +115,7 @@ export namespace MeshStreaming {
|
||||
private id: string;
|
||||
private ref: string = '';
|
||||
public parentData: MeshServerInfo.Data;
|
||||
private metadata?: Metadata;
|
||||
private metadata?: MetadataWrapper;
|
||||
public visuals?: { [tag: string]: VisualInfo };
|
||||
public backgroundSegments: { [segmentId: number]: boolean } = {};
|
||||
private focusObservable = this.plugin.behaviors.interaction.click.pipe( // QUESTION is this OK way to get focused segment?
|
||||
@@ -165,7 +164,8 @@ export namespace MeshStreaming {
|
||||
|
||||
if (!this.metadata) {
|
||||
const response = await fetch(this.getMetadataUrl());
|
||||
this.metadata = await response.json();
|
||||
const rawMetadata = await response.json();
|
||||
this.metadata = new MetadataWrapper(rawMetadata);
|
||||
}
|
||||
|
||||
if (!this.visuals) {
|
||||
@@ -209,13 +209,10 @@ export namespace MeshStreaming {
|
||||
}
|
||||
|
||||
private initVisualInfos() {
|
||||
const namesAndColors = Metadata.namesAndColorsBySegment(this.metadata!);
|
||||
|
||||
const visuals: { [tag: string]: VisualInfo } = {};
|
||||
for (const segid of Metadata.meshSegments(this.metadata!)) {
|
||||
if (DEBUG_IGNORED_SEGMENTS.has(segid)) continue;
|
||||
const name = namesAndColors[segid]?.name ?? DEFAULT_SEGMENT_NAME;
|
||||
const color = namesAndColors[segid]?.color ?? DEFAULT_SEGMENT_COLOR;
|
||||
for (const segid of this.metadata!.meshSegmentIds) {
|
||||
const name = this.metadata?.getSegment(segid)?.biological_annotation.name ?? DEFAULT_SEGMENT_NAME;
|
||||
const color = this.metadata?.getSegmentColor(segid) ?? DEFAULT_SEGMENT_COLOR;
|
||||
for (const detailType of VisualInfo.DetailTypes) {
|
||||
const visual: VisualInfo = {
|
||||
tag: VisualInfo.tagFor(segid, detailType),
|
||||
@@ -254,7 +251,7 @@ export namespace MeshStreaming {
|
||||
const visual = this.visuals[tag];
|
||||
const preferredDetail = (visual.detailType === 'high') ? highDetail : lowDetail;
|
||||
if (preferredDetail !== undefined) {
|
||||
visual.detail = Metadata.getSufficientDetail(this.metadata!, visual.segmentId, preferredDetail);
|
||||
visual.detail = this.metadata!.getSufficientMeshDetail(visual.segmentId, preferredDetail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { PluginStateObject } from '../../../mol-plugin-state/objects';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
import { Choice } from '../choice';
|
||||
import { Choice } from '../../volumes-and-segmentations/helpers';
|
||||
|
||||
|
||||
export const DEFAULT_MESH_SERVER = 'http://localhost:9000/v2';
|
||||
|
||||
@@ -13,15 +13,11 @@ import { Task } from '../../../mol-task';
|
||||
import { shallowEqualObjects } from '../../../mol-util';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
import { VolsegTransform, MeshlistData } from '../mesh-extension';
|
||||
import { BACKGROUND_OPACITY, FOREROUND_OPACITY, MeshlistData, VolsegTransform } from '../mesh-extension';
|
||||
import { MeshStreaming, NO_SEGMENT } from './behavior';
|
||||
import { MeshServerInfo } from './server-info';
|
||||
|
||||
|
||||
export const BACKGROUND_OPACITY = 0.2;
|
||||
export const FOREROUND_OPACITY = 1;
|
||||
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // //
|
||||
|
||||
export const MeshServerTransformer = VolsegTransform({
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
|
||||
// TODO unify with Metadata in Volseg
|
||||
|
||||
export interface Metadata {
|
||||
grid: {
|
||||
general: {
|
||||
details: string,
|
||||
},
|
||||
volumes: Volumes,
|
||||
segmentation_lattices: SegmentationLattices,
|
||||
segmentation_meshes: SegmentationMeshes,
|
||||
},
|
||||
annotation: Annotation,
|
||||
}
|
||||
|
||||
export interface Volumes {
|
||||
volume_downsamplings: number[],
|
||||
voxel_size: { [downsampling: number]: Vector3 },
|
||||
origin: Vector3,
|
||||
grid_dimensions: Vector3,
|
||||
sampled_grid_dimensions: { [downsampling: number]: Vector3 },
|
||||
mean: { [downsampling: number]: number },
|
||||
std: { [downsampling: number]: number },
|
||||
min: { [downsampling: number]: number },
|
||||
max: { [downsampling: number]: number },
|
||||
volume_force_dtype: string,
|
||||
}
|
||||
|
||||
export interface SegmentationLattices {
|
||||
segmentation_lattice_ids: number[],
|
||||
segmentation_downsamplings: { [lattice: number]: number[] },
|
||||
}
|
||||
|
||||
export interface Annotation {
|
||||
name: string,
|
||||
details: string,
|
||||
segment_list: Segment[],
|
||||
}
|
||||
|
||||
export interface Segment {
|
||||
id: number,
|
||||
colour: number[],
|
||||
biological_annotation: BiologicalAnnotation,
|
||||
}
|
||||
|
||||
export interface BiologicalAnnotation {
|
||||
name: string,
|
||||
external_references: { id: number, resource: string, accession: string, label: string, description: string }[]
|
||||
}
|
||||
|
||||
export interface SegmentationMeshes {
|
||||
mesh_component_numbers: {
|
||||
segment_ids?: {
|
||||
[segId: number]: {
|
||||
detail_lvls: {
|
||||
[detail: number]: {
|
||||
mesh_ids: {
|
||||
[meshId: number]: {
|
||||
num_triangles: number,
|
||||
num_vertices: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
detail_lvl_to_fraction: {
|
||||
[lvl: number]: number
|
||||
}
|
||||
}
|
||||
|
||||
type Vector3 = [number, number, number];
|
||||
|
||||
|
||||
|
||||
export namespace Metadata {
|
||||
export function meshSegments(metadata: Metadata): number[] {
|
||||
const segmentIds = metadata.grid.segmentation_meshes.mesh_component_numbers.segment_ids;
|
||||
if (segmentIds === undefined) return [];
|
||||
return Object.keys(segmentIds).map(s => parseInt(s));
|
||||
}
|
||||
export function meshSegmentDetails(metadata: Metadata, segmentId: number): number[] {
|
||||
const segmentIds = metadata.grid.segmentation_meshes.mesh_component_numbers.segment_ids;
|
||||
if (segmentIds === undefined) return [];
|
||||
const details = segmentIds[segmentId].detail_lvls;
|
||||
return Object.keys(details).map(s => parseInt(s));
|
||||
}
|
||||
/** Get the worst available detail level that is not worse than preferredDetail.
|
||||
* If preferredDetail is null, get the worst detail level overall.
|
||||
* (worse = greater number) */
|
||||
export function getSufficientDetail(metadata: Metadata, segmentId: number, preferredDetail: number | null) {
|
||||
let availDetails = meshSegmentDetails(metadata, segmentId);
|
||||
if (preferredDetail !== null) {
|
||||
availDetails = availDetails.filter(det => det <= preferredDetail);
|
||||
}
|
||||
return Math.max(...availDetails);
|
||||
}
|
||||
export function annotationsBySegment(metadata: Metadata): { [id: number]: Segment } {
|
||||
const result: { [id: number]: Segment } = {};
|
||||
for (const segment of metadata.annotation.segment_list) {
|
||||
if (segment.id in result) {
|
||||
throw new Error(`Duplicate segment annotation for segment ${segment.id}`);
|
||||
}
|
||||
result[segment.id] = segment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function dropSegments(metadata: Metadata, segments: number[]): void {
|
||||
if (metadata.grid.segmentation_meshes.mesh_component_numbers.segment_ids === undefined) return;
|
||||
const dropSet = new Set(segments);
|
||||
metadata.annotation.segment_list = metadata.annotation.segment_list.filter(seg => !dropSet.has(seg.id));
|
||||
for (const seg of segments) {
|
||||
delete metadata.grid.segmentation_meshes.mesh_component_numbers.segment_ids[seg];
|
||||
}
|
||||
}
|
||||
export function namesAndColorsBySegment(metadata: Metadata) {
|
||||
const result: { [id: number]: { name: string, color: Color } } = {};
|
||||
for (const segment of metadata.annotation.segment_list) {
|
||||
if (segment.id in result) throw new Error(`Duplicate segment annotation for segment ${segment.id}`);
|
||||
result[segment.id] = { name: segment.biological_annotation.name, color: Color.fromNormalizedArray(segment.colour, 0) };
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export { StructureQualityReport };
|
||||
type StructureQualityReport = PropertyWrapper<{
|
||||
issues: IndexedCustomProperty.Residue<string[]>,
|
||||
issueTypes: string[]
|
||||
}| undefined>
|
||||
} | undefined>
|
||||
|
||||
namespace StructureQualityReport {
|
||||
export const DefaultServerUrl = 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/';
|
||||
|
||||
@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
// Generated on 2022-12-03T21:55:37-08:00
|
||||
// Generated on 2023-01-15T10:00:07-08:00
|
||||
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
@@ -8487,6 +8487,8 @@ export type RcsbCompModelProvenance = {
|
||||
readonly source_db?: Maybe<Scalars['String']>;
|
||||
/** Source filename for the computed structure model. */
|
||||
readonly source_filename?: Maybe<Scalars['String']>;
|
||||
/** Source URL for computed structure model predicted aligned error (PAE) json file. */
|
||||
readonly source_pae_url?: Maybe<Scalars['String']>;
|
||||
/** Source URL for computed structure model file. */
|
||||
readonly source_url?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
@@ -12,8 +12,8 @@ import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
|
||||
import { createMeshFromUrl } from '../meshes/examples';
|
||||
import { BACKGROUND_SEGMENT_VOLUME_THRESHOLD } from '../meshes/mesh-streaming/behavior';
|
||||
import { createMeshFromUrl } from '../meshes/mesh-extension';
|
||||
|
||||
import { Segment } from './volseg-api/data';
|
||||
import { VolsegEntryData } from './entry-root';
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Metadata, Segment } from './data';
|
||||
|
||||
|
||||
@@ -33,6 +34,11 @@ export class MetadataWrapper {
|
||||
return this.segmentMap[segmentId];
|
||||
}
|
||||
|
||||
getSegmentColor(segmentId: number): Color | undefined {
|
||||
const colorArray = this.getSegment(segmentId)?.colour;
|
||||
return colorArray ? Color.fromNormalizedArray(colorArray, 0) : undefined;
|
||||
}
|
||||
|
||||
/** Get the list of detail levels available for the given mesh segment. */
|
||||
getMeshDetailLevels(segmentId: number): number[] {
|
||||
const segmentIds = this.raw.grid.segmentation_meshes.mesh_component_numbers.segment_ids;
|
||||
@@ -65,4 +71,4 @@ export class MetadataWrapper {
|
||||
return vx * vy * vz * gx * gy * gz;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -119,11 +119,11 @@ class Camera implements ICamera {
|
||||
return Camera.targetDistance(radius, this.state.fov, this.viewport.width, this.viewport.height);
|
||||
}
|
||||
|
||||
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3, snapshot?: Partial<Camera.Snapshot>): Partial<Camera.Snapshot> {
|
||||
const r = Math.max(radius, 0.01);
|
||||
const targetDistance = this.getTargetDistance(r);
|
||||
|
||||
Vec3.sub(this.deltaDirection, this.target, this.position);
|
||||
Vec3.sub(this.deltaDirection, snapshot?.target ?? this.target, snapshot?.position ?? this.position);
|
||||
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection);
|
||||
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance);
|
||||
Vec3.sub(this.newPosition, target, this.deltaDirection);
|
||||
@@ -137,6 +137,18 @@ class Camera implements ICamera {
|
||||
return state;
|
||||
}
|
||||
|
||||
getCenter(target: Vec3, radius?: number): Partial<Camera.Snapshot> {
|
||||
Vec3.sub(this.deltaDirection, this.target, this.position);
|
||||
Vec3.sub(this.newPosition, target, this.deltaDirection);
|
||||
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
|
||||
state.target = Vec3.clone(target);
|
||||
state.position = Vec3.clone(this.newPosition);
|
||||
if (radius) state.radius = Math.max(radius, 0.01);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
getInvariantFocus(target: Vec3, radius: number, up: Vec3, dir: Vec3): Partial<Camera.Snapshot> {
|
||||
const r = Math.max(radius, 0.01);
|
||||
const targetDistance = this.getTargetDistance(r);
|
||||
@@ -160,6 +172,10 @@ class Camera implements ICamera {
|
||||
}
|
||||
}
|
||||
|
||||
center(target: Vec3, durationMs?: number) {
|
||||
this.setState(this.getCenter(target), durationMs);
|
||||
}
|
||||
|
||||
/** Transform point into 2D window coordinates. */
|
||||
project(out: Vec4, point: Vec3) {
|
||||
return cameraProject(out, point, this.viewport, this.projectionView);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -31,7 +31,7 @@ import { PickData } from './passes/pick';
|
||||
import { PickHelper } from './passes/pick';
|
||||
import { ImagePass, ImageProps } from './passes/image';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { isDebugMode, isTimingMode } from '../mol-util/debug';
|
||||
import { addConsoleStatsProvider, isDebugMode, isTimingMode, removeConsoleStatsProvider } from '../mol-util/debug';
|
||||
import { CameraHelperParams } from './helper/camera-helper';
|
||||
import { produce } from 'immer';
|
||||
import { HandleHelperParams } from './helper/handle-helper';
|
||||
@@ -260,6 +260,7 @@ interface Canvas3D {
|
||||
notifyDidDraw: boolean,
|
||||
readonly didDraw: BehaviorSubject<now.Timestamp>
|
||||
readonly commited: BehaviorSubject<now.Timestamp>
|
||||
readonly commitQueueSize: BehaviorSubject<number>
|
||||
readonly reprCount: BehaviorSubject<number>
|
||||
readonly resized: BehaviorSubject<any>
|
||||
|
||||
@@ -306,6 +307,7 @@ namespace Canvas3D {
|
||||
let startTime = now();
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
|
||||
const commited = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
|
||||
const commitQueueSize = new BehaviorSubject<number>(0);
|
||||
|
||||
const { gl, contextRestored } = webgl;
|
||||
|
||||
@@ -440,7 +442,7 @@ namespace Canvas3D {
|
||||
cam = stereoCamera;
|
||||
}
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('Canvas3D.render');
|
||||
if (isTimingMode) webgl.timer.mark('Canvas3D.render', true);
|
||||
const ctx = { renderer, camera: cam, scene, helper };
|
||||
if (MultiSamplePass.isEnabled(p.multiSample)) {
|
||||
const forceOn = !cameraChanged && markingUpdated && !controls.isAnimating;
|
||||
@@ -593,7 +595,11 @@ namespace Canvas3D {
|
||||
// snapshot the current bounding sphere of visible objects
|
||||
Sphere3D.copy(oldBoundingSphereVisible, scene.boundingSphereVisible);
|
||||
|
||||
if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false;
|
||||
if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) {
|
||||
commitQueueSize.next(scene.commitQueueSize);
|
||||
return false;
|
||||
}
|
||||
commitQueueSize.next(0);
|
||||
|
||||
if (helper.debug.isEnabled) helper.debug.update();
|
||||
if (!p.camera.manualReset && (reprCount.value === 0 || shouldResetCamera())) {
|
||||
@@ -623,6 +629,8 @@ namespace Canvas3D {
|
||||
attribute: `${(attribute / 1024 / 1024).toFixed(3)} MiB`,
|
||||
elements: `${(elements / 1024 / 1024).toFixed(3)} MiB`,
|
||||
});
|
||||
|
||||
console.log(webgl.timer.formatedStats());
|
||||
}
|
||||
|
||||
function add(repr: Representation.Any) {
|
||||
@@ -728,6 +736,8 @@ namespace Canvas3D {
|
||||
resized.next(+new Date());
|
||||
}
|
||||
|
||||
addConsoleStatsProvider(consoleStats);
|
||||
|
||||
return {
|
||||
webgl,
|
||||
|
||||
@@ -790,6 +800,7 @@ namespace Canvas3D {
|
||||
set notifyDidDraw(v: boolean) { notifyDidDraw = v; },
|
||||
didDraw,
|
||||
commited,
|
||||
commitQueueSize,
|
||||
reprCount,
|
||||
resized,
|
||||
setProps: (properties, doNotRequestDraw = false) => {
|
||||
@@ -905,6 +916,8 @@ namespace Canvas3D {
|
||||
controls.dispose();
|
||||
renderer.dispose();
|
||||
interactionHelper.dispose();
|
||||
|
||||
removeConsoleStatsProvider(consoleStats);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,6 +11,8 @@ import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
|
||||
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
||||
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { Text } from '../../mol-geo/geometry/text/text';
|
||||
import { TextBuilder } from '../../mol-geo/geometry/text/text-builder';
|
||||
import { GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
import { Scene } from '../../mol-gl/scene';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
@@ -23,19 +25,36 @@ import { Visual } from '../../mol-repr/visual';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { assertUnreachable } from '../../mol-util/type-helpers';
|
||||
import { Camera, ICamera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
// TODO add scale line/grid
|
||||
|
||||
const AxesParams = {
|
||||
...Mesh.Params,
|
||||
alpha: { ...Mesh.Params.alpha, defaultValue: 0.51 },
|
||||
ignoreLight: { ...Mesh.Params.ignoreLight, defaultValue: true },
|
||||
alpha: PD.Numeric(0.51, { min: 0, max: 1, step: 0.01 }, { isEssential: true, label: 'Opacity' }),
|
||||
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 }),
|
||||
location: PD.Select('bottom-left', PD.arrayToOptions(['bottom-left', 'bottom-right', 'top-left', 'top-right'] as const)),
|
||||
locationOffsetX: PD.Numeric(0),
|
||||
locationOffsetY: PD.Numeric(0),
|
||||
originColor: PD.Color(ColorNames.grey),
|
||||
radiusScale: PD.Numeric(0.075, { min: 0.01, max: 0.3, step: 0.001 }),
|
||||
showPlanes: PD.Boolean(true),
|
||||
planeColorXY: PD.Color(ColorNames.grey, { label: 'Plane Color XY' }),
|
||||
planeColorXZ: PD.Color(ColorNames.grey, { label: 'Plane Color XZ' }),
|
||||
planeColorYZ: PD.Color(ColorNames.grey, { label: 'Plane Color YZ' }),
|
||||
showLabels: PD.Boolean(false),
|
||||
labelX: PD.Text('X'),
|
||||
labelY: PD.Text('Y'),
|
||||
labelZ: PD.Text('Z'),
|
||||
labelColorX: PD.Color(ColorNames.grey),
|
||||
labelColorY: PD.Color(ColorNames.grey),
|
||||
labelColorZ: PD.Color(ColorNames.grey),
|
||||
labelOpacity: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
|
||||
labelScale: PD.Numeric(0.25, { min: 0.1, max: 1.0, step: 0.01 }),
|
||||
};
|
||||
type AxesParams = typeof AxesParams
|
||||
type AxesProps = PD.Values<AxesParams>
|
||||
@@ -56,7 +75,9 @@ export class CameraHelper {
|
||||
axes: { name: 'off', params: {} }
|
||||
};
|
||||
|
||||
private renderObject: GraphicsRenderObject | undefined;
|
||||
private meshRenderObject: GraphicsRenderObject | undefined;
|
||||
private textRenderObject: GraphicsRenderObject | undefined;
|
||||
private pixelRatio = 1;
|
||||
|
||||
constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
|
||||
this.scene = Scene.create(webgl, GraphicsRenderVariantsBlended);
|
||||
@@ -74,9 +95,20 @@ export class CameraHelper {
|
||||
p.axes.name = props.axes.name;
|
||||
if (props.axes.name === 'on') {
|
||||
this.scene.clear();
|
||||
const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio };
|
||||
this.renderObject = createAxesRenderObject(params);
|
||||
this.scene.add(this.renderObject);
|
||||
this.pixelRatio = this.webgl.pixelRatio;
|
||||
const params = {
|
||||
...props.axes.params,
|
||||
scale: props.axes.params.scale * this.pixelRatio,
|
||||
labelScale: props.axes.params.labelScale * this.pixelRatio,
|
||||
};
|
||||
this.meshRenderObject = createMeshRenderObject(params);
|
||||
this.scene.add(this.meshRenderObject);
|
||||
if (props.axes.params.showLabels) {
|
||||
this.textRenderObject = createTextRenderObject(params);
|
||||
this.scene.add(this.textRenderObject);
|
||||
} else {
|
||||
this.textRenderObject = undefined;
|
||||
}
|
||||
this.scene.commit();
|
||||
|
||||
Vec3.set(this.camera.position, 0, 0, params.scale * 200);
|
||||
@@ -94,19 +126,29 @@ export class CameraHelper {
|
||||
|
||||
getLoci(pickingId: PickingId) {
|
||||
const { objectId, groupId, instanceId } = pickingId;
|
||||
if (!this.renderObject || objectId !== this.renderObject.id || groupId === CameraHelperAxis.None) return EmptyLoci;
|
||||
if ((
|
||||
(!this.meshRenderObject || objectId !== this.meshRenderObject.id) &&
|
||||
(!this.textRenderObject || objectId !== this.textRenderObject.id)
|
||||
) || groupId === CameraHelperAxis.None) return EmptyLoci;
|
||||
return CameraAxesLoci(this, groupId, instanceId);
|
||||
}
|
||||
|
||||
private eachGroup = (loci: Loci, apply: (interval: Interval) => boolean): boolean => {
|
||||
if (!this.renderObject) return false;
|
||||
if (!isCameraAxesLoci(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;
|
||||
if (this.meshRenderObject) {
|
||||
const groupCount = this.meshRenderObject.values.uGroupCount.ref.value;
|
||||
for (const { groupId, instanceId } of loci.elements) {
|
||||
const idx = instanceId * groupCount + groupId;
|
||||
if (apply(Interval.ofSingleton(idx))) changed = true;
|
||||
}
|
||||
}
|
||||
if (this.textRenderObject) {
|
||||
const groupCount = this.textRenderObject.values.uGroupCount.ref.value;
|
||||
for (const { groupId, instanceId } of loci.elements) {
|
||||
const idx = instanceId * groupCount + groupId;
|
||||
if (apply(Interval.ofSingleton(idx))) changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
@@ -117,42 +159,82 @@ export class CameraHelper {
|
||||
if (!isCameraAxesLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
}
|
||||
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
|
||||
return (
|
||||
Visual.mark(this.meshRenderObject, loci, action, this.eachGroup) ||
|
||||
Visual.mark(this.textRenderObject, loci, action, this.eachGroup)
|
||||
);
|
||||
}
|
||||
|
||||
update(camera: ICamera) {
|
||||
if (!this.renderObject) return;
|
||||
if (!this.meshRenderObject || this.props.axes.name === 'off') return;
|
||||
|
||||
if (this.pixelRatio !== this.webgl.pixelRatio) {
|
||||
this.setProps(this.props);
|
||||
}
|
||||
|
||||
updateCamera(this.camera, camera.viewport, camera.viewOffset);
|
||||
Mat4.extractRotation(this.scene.view, camera.view);
|
||||
|
||||
const r = this.renderObject.values.boundingSphere.ref.value.radius;
|
||||
Mat4.setTranslation(this.scene.view, Vec3.create(
|
||||
-camera.viewport.width / 2 + r,
|
||||
-camera.viewport.height / 2 + r,
|
||||
0
|
||||
));
|
||||
const r = this.textRenderObject
|
||||
? this.textRenderObject.values.boundingSphere.ref.value.radius
|
||||
: this.meshRenderObject.values.boundingSphere.ref.value.radius;
|
||||
const l = this.props.axes.params.location;
|
||||
const ox = this.props.axes.params.locationOffsetX * this.pixelRatio;
|
||||
const oy = this.props.axes.params.locationOffsetY * this.pixelRatio;
|
||||
if (l === 'bottom-left') {
|
||||
Mat4.setTranslation(this.scene.view, Vec3.create(
|
||||
-camera.viewport.width / 2 + r + ox,
|
||||
-camera.viewport.height / 2 + r + oy,
|
||||
0
|
||||
));
|
||||
} else if (l === 'bottom-right') {
|
||||
Mat4.setTranslation(this.scene.view, Vec3.create(
|
||||
camera.viewport.width / 2 - r - ox,
|
||||
-camera.viewport.height / 2 + r + oy,
|
||||
0
|
||||
));
|
||||
} else if (l === 'top-left') {
|
||||
Mat4.setTranslation(this.scene.view, Vec3.create(
|
||||
-camera.viewport.width / 2 + r + ox,
|
||||
camera.viewport.height / 2 - r - oy,
|
||||
0
|
||||
));
|
||||
} else if (l === 'top-right') {
|
||||
Mat4.setTranslation(this.scene.view, Vec3.create(
|
||||
camera.viewport.width / 2 - r - ox,
|
||||
camera.viewport.height / 2 - r - oy,
|
||||
0
|
||||
));
|
||||
} else {
|
||||
assertUnreachable(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const enum CameraHelperAxis {
|
||||
export enum CameraHelperAxis {
|
||||
None = 0,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
XY,
|
||||
XZ,
|
||||
YZ
|
||||
YZ,
|
||||
Origin
|
||||
}
|
||||
|
||||
function getAxisLabel(axis: number) {
|
||||
function getAxisLabel(axis: number, cameraHelper: CameraHelper) {
|
||||
const a = cameraHelper.props.axes;
|
||||
const x = a.name === 'on' ? a.params.labelX : 'X';
|
||||
const y = a.name === 'on' ? a.params.labelY : 'Y';
|
||||
const z = a.name === 'on' ? a.params.labelZ : 'Z';
|
||||
switch (axis) {
|
||||
case CameraHelperAxis.X: return 'X Axis';
|
||||
case CameraHelperAxis.Y: return 'Y Axis';
|
||||
case CameraHelperAxis.Z: return 'Z Axis';
|
||||
case CameraHelperAxis.XY: return 'XY Plane';
|
||||
case CameraHelperAxis.XZ: return 'XZ Plane';
|
||||
case CameraHelperAxis.YZ: return 'YZ Plane';
|
||||
case CameraHelperAxis.X: return `${x} Axis`;
|
||||
case CameraHelperAxis.Y: return `${y} Axis`;
|
||||
case CameraHelperAxis.Z: return `${z} Axis`;
|
||||
case CameraHelperAxis.XY: return `${x}${y} Plane`;
|
||||
case CameraHelperAxis.XZ: return `${x}${z} Plane`;
|
||||
case CameraHelperAxis.YZ: return `${y}${z} Plane`;
|
||||
case CameraHelperAxis.Origin: return 'Origin';
|
||||
default: return 'Axes';
|
||||
}
|
||||
}
|
||||
@@ -160,7 +242,7 @@ function getAxisLabel(axis: number) {
|
||||
function CameraAxesLoci(cameraHelper: CameraHelper, groupId: number, instanceId: number) {
|
||||
return DataLoci('camera-axes', cameraHelper, [{ groupId, instanceId }],
|
||||
void 0 /** bounding sphere */,
|
||||
() => getAxisLabel(groupId));
|
||||
() => getAxisLabel(groupId, cameraHelper));
|
||||
}
|
||||
export type CameraAxesLoci = ReturnType<typeof CameraAxesLoci>
|
||||
export function isCameraAxesLoci(x: Loci): x is CameraAxesLoci {
|
||||
@@ -197,15 +279,17 @@ function updateCamera(camera: Camera, viewport: Viewport, viewOffset: Camera.Vie
|
||||
Mat4.ortho(camera.projection, left, right, top, bottom, near, far);
|
||||
}
|
||||
|
||||
function createAxesMesh(scale: number, mesh?: Mesh) {
|
||||
function createAxesMesh(props: AxesProps, mesh?: Mesh) {
|
||||
const state = MeshBuilder.createState(512, 256, mesh);
|
||||
const radius = 0.075 * 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 scale = 100 * props.scale;
|
||||
const radius = props.radiusScale * scale;
|
||||
const textScale = props.showLabels ? 100 * props.labelScale / 3 : 0;
|
||||
const x = Vec3.scale(Vec3(), Vec3.unitX, scale - textScale);
|
||||
const y = Vec3.scale(Vec3(), Vec3.unitY, scale - textScale);
|
||||
const z = Vec3.scale(Vec3(), Vec3.unitZ, scale - textScale);
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
|
||||
|
||||
state.currentGroup = CameraHelperAxis.None;
|
||||
state.currentGroup = CameraHelperAxis.Origin;
|
||||
addSphere(state, Vec3.origin, radius, 2);
|
||||
|
||||
state.currentGroup = CameraHelperAxis.X;
|
||||
@@ -220,50 +304,102 @@ function createAxesMesh(scale: number, mesh?: Mesh) {
|
||||
addSphere(state, z, radius, 2);
|
||||
addCylinder(state, Vec3.origin, z, 1, cylinderProps);
|
||||
|
||||
Vec3.scale(x, x, 0.5);
|
||||
Vec3.scale(y, y, 0.5);
|
||||
Vec3.scale(z, z, 0.5);
|
||||
if (props.showPlanes) {
|
||||
Vec3.scale(x, x, 0.5);
|
||||
Vec3.scale(y, y, 0.5);
|
||||
Vec3.scale(z, z, 0.5);
|
||||
|
||||
state.currentGroup = CameraHelperAxis.XY;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, x, y);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, y, x);
|
||||
const xy = Vec3.add(Vec3(), x, y);
|
||||
MeshBuilder.addTriangle(state, xy, x, y);
|
||||
MeshBuilder.addTriangle(state, xy, y, x);
|
||||
state.currentGroup = CameraHelperAxis.XY;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, x, y);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, y, x);
|
||||
const xy = Vec3.add(Vec3(), x, y);
|
||||
MeshBuilder.addTriangle(state, xy, x, y);
|
||||
MeshBuilder.addTriangle(state, xy, y, x);
|
||||
|
||||
state.currentGroup = CameraHelperAxis.XZ;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, x, z);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, z, x);
|
||||
const xz = Vec3.add(Vec3(), x, z);
|
||||
MeshBuilder.addTriangle(state, xz, x, z);
|
||||
MeshBuilder.addTriangle(state, xz, z, x);
|
||||
state.currentGroup = CameraHelperAxis.XZ;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, x, z);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, z, x);
|
||||
const xz = Vec3.add(Vec3(), x, z);
|
||||
MeshBuilder.addTriangle(state, xz, x, z);
|
||||
MeshBuilder.addTriangle(state, xz, z, x);
|
||||
|
||||
state.currentGroup = CameraHelperAxis.YZ;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, y, z);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, z, y);
|
||||
const yz = Vec3.add(Vec3(), y, z);
|
||||
MeshBuilder.addTriangle(state, yz, y, z);
|
||||
MeshBuilder.addTriangle(state, yz, z, y);
|
||||
state.currentGroup = CameraHelperAxis.YZ;
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, y, z);
|
||||
MeshBuilder.addTriangle(state, Vec3.origin, z, y);
|
||||
const yz = Vec3.add(Vec3(), y, z);
|
||||
MeshBuilder.addTriangle(state, yz, y, z);
|
||||
MeshBuilder.addTriangle(state, yz, z, y);
|
||||
}
|
||||
|
||||
return MeshBuilder.getMesh(state);
|
||||
}
|
||||
|
||||
function getAxesShape(props: AxesProps, shape?: Shape<Mesh>) {
|
||||
function getAxesMeshShape(props: AxesProps, shape?: Shape<Mesh>) {
|
||||
const scale = 100 * props.scale;
|
||||
const mesh = createAxesMesh(scale, shape?.geometry);
|
||||
const mesh = createAxesMesh(props, shape?.geometry);
|
||||
mesh.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale + scale / 4));
|
||||
const getColor = (groupId: number) => {
|
||||
switch (groupId) {
|
||||
case 1: return props.colorX;
|
||||
case 2: return props.colorY;
|
||||
case 3: return props.colorZ;
|
||||
case CameraHelperAxis.X: return props.colorX;
|
||||
case CameraHelperAxis.Y: return props.colorY;
|
||||
case CameraHelperAxis.Z: return props.colorZ;
|
||||
case CameraHelperAxis.XY: return props.planeColorXY;
|
||||
case CameraHelperAxis.XZ: return props.planeColorXZ;
|
||||
case CameraHelperAxis.YZ: return props.planeColorYZ;
|
||||
case CameraHelperAxis.Origin: return props.originColor;
|
||||
default: return ColorNames.grey;
|
||||
}
|
||||
};
|
||||
return Shape.create('axes', {}, mesh, getColor, () => 1, () => '');
|
||||
return Shape.create('axes-mesh', {}, mesh, getColor, () => 1, () => '');
|
||||
}
|
||||
|
||||
function createAxesRenderObject(props: AxesProps) {
|
||||
const shape = getAxesShape(props);
|
||||
return Shape.createRenderObject(shape, props);
|
||||
}
|
||||
function createMeshRenderObject(props: AxesProps) {
|
||||
const shape = getAxesMeshShape(props);
|
||||
return Shape.createRenderObject(shape, {
|
||||
...PD.getDefaultValues(Mesh.Params),
|
||||
...props,
|
||||
ignoreLight: true,
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function createAxesText(props: AxesProps, text?: Text) {
|
||||
const builder = TextBuilder.create(props, 8, 8, text);
|
||||
const scale = 100 * props.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 textScale = 100 * props.labelScale;
|
||||
builder.add(props.labelX, x[0], x[1], x[2], 0.0, textScale, CameraHelperAxis.X);
|
||||
builder.add(props.labelY, y[0], y[1], y[2], 0.0, textScale, CameraHelperAxis.Y);
|
||||
builder.add(props.labelZ, z[0], z[1], z[2], 0.0, textScale, CameraHelperAxis.Z);
|
||||
|
||||
return builder.getText();
|
||||
}
|
||||
|
||||
function getAxesTextShape(props: AxesProps, shape?: Shape<Text>) {
|
||||
const scale = 100 * props.scale;
|
||||
const text = createAxesText(props, shape?.geometry);
|
||||
text.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale));
|
||||
const getColor = (groupId: number) => {
|
||||
switch (groupId) {
|
||||
case CameraHelperAxis.X: return props.labelColorX;
|
||||
case CameraHelperAxis.Y: return props.labelColorY;
|
||||
case CameraHelperAxis.Z: return props.labelColorZ;
|
||||
default: return ColorNames.grey;
|
||||
}
|
||||
};
|
||||
return Shape.create('axes-text', {}, text, getColor, () => 1, () => '');
|
||||
}
|
||||
|
||||
function createTextRenderObject(props: AxesProps) {
|
||||
const shape = getAxesTextShape(props);
|
||||
return Shape.createRenderObject(shape, {
|
||||
...PD.getDefaultValues(Text.Params),
|
||||
...props,
|
||||
alpha: props.labelOpacity,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -54,6 +54,7 @@ export class HandleHelper {
|
||||
};
|
||||
|
||||
private renderObject: GraphicsRenderObject | undefined;
|
||||
private pixelRatio = 1;
|
||||
|
||||
private _transform = Mat4();
|
||||
getBoundingSphere(out: Sphere3D, instanceId: number) {
|
||||
@@ -71,7 +72,11 @@ export class HandleHelper {
|
||||
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.pixelRatio = this.webgl.pixelRatio;
|
||||
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();
|
||||
@@ -91,6 +96,10 @@ export class HandleHelper {
|
||||
update(camera: Camera, position: Vec3, rotation: Mat3) {
|
||||
if (!this.renderObject) return;
|
||||
|
||||
if (this.pixelRatio !== this.webgl.pixelRatio) {
|
||||
this.setProps(this.props);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
|
||||
type DragEvent = import('../canvas3d').Canvas3D.DragEvent
|
||||
type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
|
||||
|
||||
const enum InputEvent { Move, Click, Drag }
|
||||
enum InputEvent { Move, Click, Drag }
|
||||
|
||||
const tmpPosA = Vec3();
|
||||
const tmpPos = Vec3();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -312,7 +312,7 @@ export class DrawPass {
|
||||
|
||||
const { x, y, width, height } = camera.viewport;
|
||||
renderer.setViewport(x, y, width, height);
|
||||
renderer.update(camera);
|
||||
renderer.update(camera, scene);
|
||||
|
||||
if (transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
|
||||
this.drawTarget.bind();
|
||||
@@ -360,7 +360,7 @@ export class DrawPass {
|
||||
}
|
||||
if (helper.camera.isEnabled) {
|
||||
helper.camera.update(camera);
|
||||
renderer.update(helper.camera.camera);
|
||||
renderer.update(helper.camera.camera, helper.camera.scene);
|
||||
renderer.renderBlended(helper.camera.scene, helper.camera.camera);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2021-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -27,6 +27,8 @@ export const MarkingParams = {
|
||||
highlightEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(1.0, 0.4, 0.6), 1.0)),
|
||||
selectEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(0.2, 1.0, 0.1), 1.0)),
|
||||
edgeScale: PD.Numeric(1, { min: 1, max: 3, step: 1 }, { description: 'Thickness of the edge.' }),
|
||||
highlightEdgeStrength: PD.Numeric(1.0, { min: 0, max: 1, step: 0.1 }),
|
||||
selectEdgeStrength: PD.Numeric(1.0, { min: 0, max: 1, step: 0.1 }),
|
||||
ghostEdgeStrength: PD.Numeric(0.3, { min: 0, max: 1, step: 0.1 }, { description: 'Opacity of the hidden edges that are covered by other geometry. When set to 1, one less geometry render pass is done.' }),
|
||||
innerEdgeFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }, { description: 'Factor to multiply the inner edge color with - for added contrast.' }),
|
||||
};
|
||||
@@ -101,7 +103,7 @@ export class MarkingPass {
|
||||
}
|
||||
|
||||
update(props: MarkingProps) {
|
||||
const { highlightEdgeColor, selectEdgeColor, edgeScale, innerEdgeFactor, ghostEdgeStrength } = props;
|
||||
const { highlightEdgeColor, selectEdgeColor, edgeScale, innerEdgeFactor, ghostEdgeStrength, highlightEdgeStrength, selectEdgeStrength } = props;
|
||||
|
||||
const { values: edgeValues } = this.edge;
|
||||
const _edgeScale = Math.round(edgeScale * this.webgl.pixelRatio);
|
||||
@@ -113,8 +115,10 @@ export class MarkingPass {
|
||||
const { values: overlayValues } = this.overlay;
|
||||
ValueCell.update(overlayValues.uHighlightEdgeColor, Color.toVec3Normalized(overlayValues.uHighlightEdgeColor.ref.value, highlightEdgeColor));
|
||||
ValueCell.update(overlayValues.uSelectEdgeColor, Color.toVec3Normalized(overlayValues.uSelectEdgeColor.ref.value, selectEdgeColor));
|
||||
ValueCell.update(overlayValues.uInnerEdgeFactor, innerEdgeFactor);
|
||||
ValueCell.update(overlayValues.uGhostEdgeStrength, ghostEdgeStrength);
|
||||
ValueCell.updateIfChanged(overlayValues.uInnerEdgeFactor, innerEdgeFactor);
|
||||
ValueCell.updateIfChanged(overlayValues.uGhostEdgeStrength, ghostEdgeStrength);
|
||||
ValueCell.updateIfChanged(overlayValues.uHighlightEdgeStrength, highlightEdgeStrength);
|
||||
ValueCell.updateIfChanged(overlayValues.uSelectEdgeStrength, selectEdgeStrength);
|
||||
}
|
||||
|
||||
render(viewport: Viewport, target: RenderTarget | undefined) {
|
||||
@@ -170,6 +174,8 @@ const OverlaySchema = {
|
||||
uTexSizeInv: UniformSpec('v2'),
|
||||
uHighlightEdgeColor: UniformSpec('v3'),
|
||||
uSelectEdgeColor: UniformSpec('v3'),
|
||||
uHighlightEdgeStrength: UniformSpec('f'),
|
||||
uSelectEdgeStrength: UniformSpec('f'),
|
||||
uGhostEdgeStrength: UniformSpec('f'),
|
||||
uInnerEdgeFactor: UniformSpec('f'),
|
||||
};
|
||||
@@ -186,6 +192,8 @@ function getOverlayRenderable(ctx: WebGLContext, edgeTexture: Texture): OverlayR
|
||||
uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
|
||||
uHighlightEdgeColor: ValueCell.create(Vec3()),
|
||||
uSelectEdgeColor: ValueCell.create(Vec3()),
|
||||
uHighlightEdgeStrength: ValueCell.create(1),
|
||||
uSelectEdgeStrength: ValueCell.create(1),
|
||||
uGhostEdgeStrength: ValueCell.create(0),
|
||||
uInnerEdgeFactor: ValueCell.create(0),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -172,7 +172,7 @@ export class PickPass {
|
||||
|
||||
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
|
||||
renderer.clear(false);
|
||||
renderer.update(camera);
|
||||
renderer.update(camera, scene);
|
||||
renderer.renderPick(scene.primitives, camera, variant, null, pickType);
|
||||
|
||||
if (helper.handle.isEnabled) {
|
||||
@@ -181,7 +181,7 @@ export class PickPass {
|
||||
|
||||
if (helper.camera.isEnabled) {
|
||||
helper.camera.update(camera);
|
||||
renderer.update(helper.camera.camera);
|
||||
renderer.update(helper.camera.camera, helper.camera.scene);
|
||||
renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null, pickType);
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export class PickHelper {
|
||||
}
|
||||
|
||||
private render(camera: Camera | StereoCamera) {
|
||||
if (isTimingMode) this.webgl.timer.mark('PickHelper.render');
|
||||
if (isTimingMode) this.webgl.timer.mark('PickHelper.render', true);
|
||||
const { pickX, pickY, pickWidth, pickHeight, halfPickWidth } = this;
|
||||
const { renderer, scene, helper } = this;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -283,6 +283,7 @@ const PostprocessingSchema = {
|
||||
uTransparentBackground: UniformSpec('b'),
|
||||
|
||||
uMaxPossibleViewZDiff: UniformSpec('f'),
|
||||
uInvProjection: UniformSpec('m4'),
|
||||
|
||||
dOcclusionEnable: DefineSpec('boolean'),
|
||||
uOcclusionOffset: UniformSpec('v2'),
|
||||
@@ -319,6 +320,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
uTransparentBackground: ValueCell.create(false),
|
||||
|
||||
uMaxPossibleViewZDiff: ValueCell.create(0.5),
|
||||
uInvProjection: ValueCell.create(Mat4.identity()),
|
||||
|
||||
dOcclusionEnable: ValueCell.create(true),
|
||||
uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)),
|
||||
@@ -450,7 +452,9 @@ export class PostprocessingPass {
|
||||
const sw = Math.floor(width * this.ssaoScale);
|
||||
const sh = Math.floor(height * this.ssaoScale);
|
||||
|
||||
this.downsampledDepthTarget = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear');
|
||||
this.downsampledDepthTarget = drawPass.packedDepth
|
||||
? webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear', 'rgba')
|
||||
: webgl.createRenderTarget(sw, sh, false, 'float32', 'linear', webgl.isWebGL2 ? 'alpha' : 'rgba');
|
||||
this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTextureOpaque);
|
||||
|
||||
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
@@ -463,7 +467,7 @@ export class PostprocessingPass {
|
||||
|
||||
this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0');
|
||||
|
||||
this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
|
||||
this.ssaoRenderable = getSsaoRenderable(webgl, this.ssaoScale === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
|
||||
this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
|
||||
this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.shadowsTarget.texture, this.outlinesTarget.texture, this.ssaoDepthTexture, true);
|
||||
@@ -581,7 +585,7 @@ export class PostprocessingPass {
|
||||
this.ssaoDepthBlurProxyTexture.define(sw, sh);
|
||||
|
||||
if (this.ssaoScale === 1) {
|
||||
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureTransparent);
|
||||
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureOpaque);
|
||||
} else {
|
||||
ValueCell.update(this.ssaoRenderable.values.tDepth, this.downsampledDepthTarget.texture);
|
||||
}
|
||||
@@ -630,7 +634,7 @@ export class PostprocessingPass {
|
||||
const transparentOutline = includeTransparent ?? true;
|
||||
// orthographic needs lower threshold
|
||||
if (camera.state.mode === 'orthographic') threshold /= 5;
|
||||
const factor = Math.pow(1000, threshold) / 1000;
|
||||
const factor = Math.pow(1000, threshold / 10) / 1000;
|
||||
// use radiusMax for stable outlines when zooming
|
||||
const maxPossibleViewZDiff = factor * camera.state.radiusMax;
|
||||
const outlineScale = props.outline.params.scale - 1;
|
||||
@@ -644,6 +648,8 @@ export class PostprocessingPass {
|
||||
ValueCell.update(this.renderable.values.uOutlineColor, Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, props.outline.params.color));
|
||||
|
||||
ValueCell.updateIfChanged(this.renderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
|
||||
ValueCell.update(this.renderable.values.uInvProjection, invProjection);
|
||||
|
||||
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; }
|
||||
ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale);
|
||||
if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateMain = true; }
|
||||
@@ -728,6 +734,7 @@ export class PostprocessingPass {
|
||||
// don't render occlusion if offset is given,
|
||||
// which will reuse the existing occlusion
|
||||
if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {
|
||||
if (isTimingMode) this.webgl.timer.mark('SSAO.render');
|
||||
if (this.ssaoScale < 1) {
|
||||
this.downsampledDepthTarget.bind();
|
||||
this.downsampleDepthRenderable.render();
|
||||
@@ -741,6 +748,7 @@ export class PostprocessingPass {
|
||||
|
||||
this.ssaoBlurSecondPassFramebuffer.bind();
|
||||
this.ssaoBlurSecondPassRenderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('SSAO.render');
|
||||
}
|
||||
|
||||
if (toDrawingBuffer) {
|
||||
|
||||
@@ -93,20 +93,35 @@ namespace Column {
|
||||
return !!v && !!(v as Column<any>).schema && !!(v as Column<any>).value;
|
||||
}
|
||||
|
||||
export const enum ValueKind {
|
||||
// Value kinds are accessed very of often
|
||||
// Using a const enum is an internal optimization and is defined separately to better support
|
||||
// compiling with isolatedModules flag in 3rd party use-cases of Mol*.
|
||||
export const enum ValueKinds {
|
||||
/** Defined value (= 0) */
|
||||
Present = 0,
|
||||
/** Expressed in CIF as `.` */
|
||||
/** Expressed in CIF as `.` (= 1) */
|
||||
NotPresent = 1,
|
||||
/** Expressed in CIF as `?` */
|
||||
/** Expressed in CIF as `?` (= 2) */
|
||||
Unknown = 2
|
||||
}
|
||||
|
||||
export const ValueKind = {
|
||||
/** Defined value (= 0) */
|
||||
Present: ValueKinds.Present,
|
||||
/** Expressed in CIF as `.` (= 1) */
|
||||
NotPresent: ValueKinds.NotPresent,
|
||||
/** Expressed in CIF as `?` (= 2) */
|
||||
Unknown: ValueKinds.Unknown
|
||||
} as const;
|
||||
export type ValueKind = (typeof ValueKind)[keyof typeof ValueKinds];
|
||||
|
||||
|
||||
export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> {
|
||||
return constColumn(schema['T'], rowCount, schema, ValueKind.NotPresent);
|
||||
return constColumn(schema['T'], rowCount, schema, ValueKinds.NotPresent);
|
||||
}
|
||||
|
||||
export function ofConst<T extends Schema>(v: T['T'], rowCount: number, type: T): Column<T['T']> {
|
||||
return constColumn(v, rowCount, type, ValueKind.Present);
|
||||
return constColumn(v, rowCount, type, ValueKinds.Present);
|
||||
}
|
||||
|
||||
export function ofLambda<T extends Schema>(spec: LambdaSpec<T>): Column<T['T']> {
|
||||
@@ -256,7 +271,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
|
||||
return {
|
||||
schema: schema,
|
||||
__array: void 0,
|
||||
isDefined: valueKind === Column.ValueKind.Present,
|
||||
isDefined: valueKind === Column.ValueKinds.Present,
|
||||
rowCount,
|
||||
value,
|
||||
valueKind: row => valueKind,
|
||||
@@ -276,7 +291,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqua
|
||||
isDefined: true,
|
||||
rowCount,
|
||||
value,
|
||||
valueKind: valueKind ? valueKind : row => Column.ValueKind.Present,
|
||||
valueKind: valueKind ? valueKind : row => Column.ValueKinds.Present,
|
||||
toArray: params => {
|
||||
const { array, start } = ColumnHelpers.createArray(rowCount, params);
|
||||
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
|
||||
@@ -304,7 +319,7 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
isDefined: true,
|
||||
rowCount,
|
||||
value,
|
||||
valueKind: valueKind ? valueKind : row => Column.ValueKind.Present,
|
||||
valueKind: valueKind ? valueKind : row => Column.ValueKinds.Present,
|
||||
toArray: schema.valueType === 'str'
|
||||
? (schema as Column.Schema.Str).transform === 'lowercase'
|
||||
? params => {
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Table {
|
||||
rowCount,
|
||||
schema: schema[k],
|
||||
value: r => rows[r][k],
|
||||
valueKind: r => typeof rows[r][k] === 'undefined' ? Column.ValueKind.NotPresent : Column.ValueKind.Present
|
||||
valueKind: r => typeof rows[r][k] === 'undefined' ? Column.ValueKinds.NotPresent : Column.ValueKinds.Present
|
||||
});
|
||||
}
|
||||
return ret as R;
|
||||
@@ -267,7 +267,7 @@ namespace Table {
|
||||
StringBuilder.write(sb, '|');
|
||||
for (let i = 0; i < cols.length; i++) {
|
||||
const c = table[cols[i]];
|
||||
if (c.valueKind(r) === Column.ValueKind.Present) {
|
||||
if (c.valueKind(r) === Column.ValueKinds.Present) {
|
||||
StringBuilder.write(sb, c.value(r));
|
||||
StringBuilder.write(sb, '|');
|
||||
} else {
|
||||
|
||||
@@ -132,8 +132,8 @@ export namespace BaseGeometry {
|
||||
ValueCell.updateIfChanged(values.uBumpiness, props.material.bumpiness);
|
||||
|
||||
const clip = Clip.getClip(props.clip);
|
||||
ValueCell.update(values.dClipObjectCount, clip.objects.count);
|
||||
ValueCell.update(values.dClipVariant, clip.variant);
|
||||
ValueCell.updateIfChanged(values.dClipObjectCount, clip.objects.count);
|
||||
ValueCell.updateIfChanged(values.dClipVariant, clip.variant);
|
||||
ValueCell.update(values.uClipObjectType, clip.objects.type);
|
||||
ValueCell.update(values.uClipObjectInvert, clip.objects.invert);
|
||||
ValueCell.update(values.uClipObjectPosition, clip.objects.position);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -214,7 +214,7 @@ export namespace Cylinders {
|
||||
|
||||
const padding = getMaxSize(size) * props.sizeFactor;
|
||||
const invariantBoundingSphere = Sphere3D.clone(cylinders.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('cylinders'),
|
||||
@@ -272,7 +272,7 @@ export namespace Cylinders {
|
||||
|
||||
function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(cylinders.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -223,7 +223,7 @@ export namespace DirectVolume {
|
||||
const counts = { drawCount: VolumeBox.indices.length, vertexCount: x * y * z, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
|
||||
const transferTex = createTransferFunctionTexture(controlPoints);
|
||||
@@ -295,7 +295,7 @@ export namespace DirectVolume {
|
||||
|
||||
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -154,7 +154,7 @@ namespace Image {
|
||||
const counts = { drawCount: QuadIndices.length, vertexCount: QuadPositions.length / 3, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(image.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('image'),
|
||||
@@ -199,7 +199,7 @@ namespace Image {
|
||||
|
||||
function updateBoundingSphere(values: ImageValues, image: Image) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(image.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -219,7 +219,7 @@ export namespace Lines {
|
||||
const counts = { drawCount: lines.lineCount * 2 * 3, vertexCount: lines.lineCount * 4, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('lines'),
|
||||
@@ -263,7 +263,7 @@ export namespace Lines {
|
||||
|
||||
function updateBoundingSphere(values: LinesValues, lines: Lines) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -680,7 +680,7 @@ export namespace Mesh {
|
||||
const counts = { drawCount: mesh.triangleCount * 3, vertexCount: mesh.vertexCount, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('mesh'),
|
||||
@@ -735,7 +735,7 @@ export namespace Mesh {
|
||||
|
||||
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -181,7 +181,7 @@ export namespace Points {
|
||||
const counts = { drawCount: points.pointCount, vertexCount: points.pointCount, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('points'),
|
||||
@@ -222,7 +222,7 @@ export namespace Points {
|
||||
|
||||
function updateBoundingSphere(values: PointsValues, points: Points) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -184,7 +184,7 @@ export namespace Spheres {
|
||||
|
||||
const padding = spheres.boundingSphere.radius ? getMaxSize(size) * props.sizeFactor : 0;
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('spheres'),
|
||||
@@ -242,7 +242,7 @@ export namespace Spheres {
|
||||
? getMaxSize(values) * values.uSizeFactor.ref.value
|
||||
: 0;
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -223,7 +223,7 @@ export namespace Text {
|
||||
|
||||
const padding = getPadding(text.mappingBuffer.ref.value, text.depthBuffer.ref.value, text.charCount, getMaxSize(size));
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('text'),
|
||||
@@ -290,7 +290,7 @@ export namespace Text {
|
||||
function updateBoundingSphere(values: TextValues, text: Text) {
|
||||
const padding = getPadding(values.aMapping.ref.value, values.aDepth.ref.value, text.charCount, getMaxSize(values));
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2021-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -7,7 +7,7 @@
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../../mol-gl/renderable';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { isNullTexture, Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { createComputeRenderItem } from '../../../mol-gl/webgl/render-item';
|
||||
import { ValueSpec, AttributeSpec, UniformSpec, TextureSpec, Values, DefineSpec } from '../../../mol-gl/renderable/schema';
|
||||
@@ -267,7 +267,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
|
||||
const [dx, dy, dz] = gridDim;
|
||||
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
|
||||
// console.log({ width, height, texCols, dim, resolution });
|
||||
// console.log({ width, height, texCols, gridDim, resolution });
|
||||
|
||||
if (!webgl.namedFramebuffers[ColorAccumulateName]) {
|
||||
webgl.namedFramebuffers[ColorAccumulateName] = webgl.resources.framebuffer();
|
||||
@@ -363,7 +363,9 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
// normalize
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('ColorNormalize.render');
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
if (!texture || isNullTexture(texture)) {
|
||||
texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
}
|
||||
texture.define(width, height);
|
||||
|
||||
const normalizeRenderable = getNormalizeRenderable(webgl, accumulateTexture, countTexture);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -187,7 +187,7 @@ export namespace TextureMesh {
|
||||
const counts = { drawCount: textureMesh.vertexCount, vertexCount: textureMesh.vertexCount, groupCount, instanceCount };
|
||||
|
||||
const invariantBoundingSphere = Sphere3D.clone(textureMesh.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
|
||||
|
||||
return {
|
||||
dGeometryType: ValueCell.create('textureMesh'),
|
||||
@@ -244,7 +244,7 @@ export namespace TextureMesh {
|
||||
|
||||
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {
|
||||
const invariantBoundingSphere = Sphere3D.clone(textureMesh.boundingSphere);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value, 0);
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere);
|
||||
|
||||
@@ -19,6 +19,10 @@ export class CommitQueue {
|
||||
return this.removeList.count === 0 && this.addList.count === 0;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.removeMap.size + this.addMap.size;
|
||||
}
|
||||
|
||||
add(o: GraphicsRenderObject) {
|
||||
if (this.removeMap.has(o)) {
|
||||
const a = this.removeMap.get(o)!;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
@@ -152,9 +152,12 @@ export const GlobalUniformSchema = {
|
||||
|
||||
uHighlightColor: UniformSpec('v3'),
|
||||
uSelectColor: UniformSpec('v3'),
|
||||
uDimColor: UniformSpec('v3'),
|
||||
uHighlightStrength: UniformSpec('f'),
|
||||
uSelectStrength: UniformSpec('f'),
|
||||
uDimStrength: UniformSpec('f'),
|
||||
uMarkerPriority: UniformSpec('i'),
|
||||
uMarkerAverage: UniformSpec('f'),
|
||||
|
||||
uXrayEdgeFalloff: UniformSpec('f'),
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -10,6 +10,10 @@ import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
import { TextureFilter } from '../webgl/texture';
|
||||
import { arrayMinMax } from '../../mol-util/array';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
const v3transformMat4Offset = Vec3.transformMat4Offset;
|
||||
|
||||
export function calculateTextureInfo(n: number, itemSize: number) {
|
||||
n = Math.max(n, 2); // observed issues with 1 pixel textures
|
||||
const sqN = Math.sqrt(n);
|
||||
@@ -137,21 +141,21 @@ export function calculateInvariantBoundingSphere(position: Float32Array, positio
|
||||
|
||||
boundaryHelper.reset();
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i);
|
||||
v3fromArray(v, position, i);
|
||||
boundaryHelper.includePosition(v);
|
||||
}
|
||||
boundaryHelper.finishedIncludeStep();
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i);
|
||||
v3fromArray(v, position, i);
|
||||
boundaryHelper.radiusPosition(v);
|
||||
}
|
||||
|
||||
const sphere = boundaryHelper.getSphere();
|
||||
|
||||
if (positionCount <= 98) {
|
||||
if (positionCount <= 14) {
|
||||
const extrema: Vec3[] = [];
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
extrema.push(Vec3.fromArray(Vec3(), position, i));
|
||||
extrema.push(v3fromArray(Vec3(), position, i));
|
||||
}
|
||||
Sphere3D.setExtrema(sphere, extrema);
|
||||
}
|
||||
@@ -161,9 +165,9 @@ export function calculateInvariantBoundingSphere(position: Float32Array, positio
|
||||
|
||||
const _mat4 = Mat4();
|
||||
|
||||
export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere3D, transform: Float32Array, transformCount: number): Sphere3D {
|
||||
export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere3D, transform: Float32Array, transformCount: number, transformOffset: number): Sphere3D {
|
||||
if (transformCount === 1) {
|
||||
Mat4.fromArray(_mat4, transform, 0);
|
||||
Mat4.fromArray(_mat4, transform, transformOffset);
|
||||
const s = Sphere3D.clone(invariantBoundingSphere);
|
||||
return Mat4.isIdentity(_mat4) ? s : Sphere3D.transform(s, s, _mat4);
|
||||
}
|
||||
@@ -174,28 +178,28 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
|
||||
const { center, radius, extrema } = invariantBoundingSphere;
|
||||
|
||||
// only use extrema if there are not too many transforms
|
||||
if (extrema && transformCount < 50) {
|
||||
if (extrema && transformCount <= 14) {
|
||||
for (let i = 0, _i = transformCount; i < _i; ++i) {
|
||||
for (const e of extrema) {
|
||||
Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16);
|
||||
v3transformMat4Offset(v, e, transform, 0, 0, i * 16 + transformOffset);
|
||||
boundaryHelper.includePosition(v);
|
||||
}
|
||||
}
|
||||
boundaryHelper.finishedIncludeStep();
|
||||
for (let i = 0, _i = transformCount; i < _i; ++i) {
|
||||
for (const e of extrema) {
|
||||
Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16);
|
||||
v3transformMat4Offset(v, e, transform, 0, 0, i * 16 + transformOffset);
|
||||
boundaryHelper.radiusPosition(v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0, _i = transformCount; i < _i; ++i) {
|
||||
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16);
|
||||
v3transformMat4Offset(v, center, transform, 0, 0, i * 16 + transformOffset);
|
||||
boundaryHelper.includePositionRadius(v, radius);
|
||||
}
|
||||
boundaryHelper.finishedIncludeStep();
|
||||
for (let i = 0, _i = transformCount; i < _i; ++i) {
|
||||
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16);
|
||||
v3transformMat4Offset(v, center, transform, 0, 0, i * 16 + transformOffset);
|
||||
boundaryHelper.radiusPositionRadius(v, radius);
|
||||
}
|
||||
}
|
||||
@@ -205,7 +209,7 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
|
||||
|
||||
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
|
||||
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount, 0);
|
||||
Sphere3D.expand(boundingSphere, boundingSphere, padding);
|
||||
Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding);
|
||||
return { boundingSphere, invariantBoundingSphere };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
@@ -38,14 +38,14 @@ export interface RendererStats {
|
||||
instancedDrawCount: number
|
||||
}
|
||||
|
||||
export const enum PickType {
|
||||
export enum PickType {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Instance = 2,
|
||||
Group = 3,
|
||||
}
|
||||
|
||||
export const enum MarkingType {
|
||||
export enum MarkingType {
|
||||
None = 0,
|
||||
Depth = 1,
|
||||
Mask = 2,
|
||||
@@ -58,7 +58,7 @@ interface Renderer {
|
||||
|
||||
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
|
||||
clearDepth: (packed?: boolean) => void
|
||||
update: (camera: ICamera) => void
|
||||
update: (camera: ICamera, scene: Scene) => void
|
||||
|
||||
renderPick: (group: Scene.Group, camera: ICamera, variant: 'pick' | 'depth', depthTexture: Texture | null, pickType: PickType) => void
|
||||
renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
@@ -97,8 +97,10 @@ export const RendererParams = {
|
||||
colorMarker: PD.Boolean(true, { description: 'Enable color marker' }),
|
||||
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
|
||||
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
|
||||
dimColor: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
|
||||
highlightStrength: PD.Numeric(0.3, { min: 0.0, max: 1.0, step: 0.1 }),
|
||||
selectStrength: PD.Numeric(0.3, { min: 0.0, max: 1.0, step: 0.1 }),
|
||||
dimStrength: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.1 }),
|
||||
markerPriority: PD.Select(1, [[1, 'Highlight'], [2, 'Select']]),
|
||||
|
||||
xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
|
||||
@@ -107,7 +109,7 @@ export const RendererParams = {
|
||||
inclination: PD.Numeric(150, { min: 0, max: 180, step: 1 }),
|
||||
azimuth: PD.Numeric(320, { min: 0, max: 360, step: 1 }),
|
||||
color: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
|
||||
intensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
intensity: PD.Numeric(0.6, { min: 0.0, max: 5.0, step: 0.01 }),
|
||||
}, o => Color.toHexString(o.color), { defaultValue: [{
|
||||
inclination: 150,
|
||||
azimuth: 320,
|
||||
@@ -115,7 +117,7 @@ export const RendererParams = {
|
||||
intensity: 0.6
|
||||
}] }),
|
||||
ambientColor: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
|
||||
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 2.0, step: 0.01 }),
|
||||
};
|
||||
export type RendererProps = PD.Values<typeof RendererParams>
|
||||
|
||||
@@ -232,9 +234,12 @@ namespace Renderer {
|
||||
|
||||
uHighlightColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.highlightColor)),
|
||||
uSelectColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.selectColor)),
|
||||
uDimColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.dimColor)),
|
||||
uHighlightStrength: ValueCell.create(p.highlightStrength),
|
||||
uSelectStrength: ValueCell.create(p.selectStrength),
|
||||
uDimStrength: ValueCell.create(p.dimStrength),
|
||||
uMarkerPriority: ValueCell.create(p.markerPriority),
|
||||
uMarkerAverage: ValueCell.create(0),
|
||||
|
||||
uXrayEdgeFalloff: ValueCell.create(p.xrayEdgeFalloff),
|
||||
};
|
||||
@@ -329,7 +334,7 @@ namespace Renderer {
|
||||
r.render(variant, sharedTexturesList.length);
|
||||
};
|
||||
|
||||
const update = (camera: ICamera) => {
|
||||
const update = (camera: ICamera, scene: Scene) => {
|
||||
ValueCell.update(globalUniforms.uView, camera.view);
|
||||
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view));
|
||||
ValueCell.update(globalUniforms.uProjection, camera.projection);
|
||||
@@ -346,13 +351,15 @@ namespace Renderer {
|
||||
ValueCell.updateIfChanged(globalUniforms.uFogFar, camera.fogFar);
|
||||
ValueCell.updateIfChanged(globalUniforms.uFogNear, camera.fogNear);
|
||||
ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground);
|
||||
|
||||
ValueCell.updateIfChanged(globalUniforms.uMarkerAverage, scene.markerAverage);
|
||||
};
|
||||
|
||||
const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderMask: Mask, markingDepthTest: boolean) => {
|
||||
arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || emptyDepthTexture);
|
||||
|
||||
ValueCell.update(globalUniforms.uModel, group.view);
|
||||
ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, group.view, camera.view));
|
||||
ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, camera.view, group.view));
|
||||
ValueCell.update(globalUniforms.uInvModelView, Mat4.invert(invModelView, modelView));
|
||||
ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection));
|
||||
ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection));
|
||||
@@ -755,6 +762,10 @@ namespace Renderer {
|
||||
p.selectColor = props.selectColor;
|
||||
ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor));
|
||||
}
|
||||
if (props.dimColor !== undefined && props.dimColor !== p.dimColor) {
|
||||
p.dimColor = props.dimColor;
|
||||
ValueCell.update(globalUniforms.uDimColor, Color.toVec3Normalized(globalUniforms.uDimColor.ref.value, p.dimColor));
|
||||
}
|
||||
if (props.highlightStrength !== undefined && props.highlightStrength !== p.highlightStrength) {
|
||||
p.highlightStrength = props.highlightStrength;
|
||||
ValueCell.update(globalUniforms.uHighlightStrength, p.highlightStrength);
|
||||
@@ -763,6 +774,10 @@ namespace Renderer {
|
||||
p.selectStrength = props.selectStrength;
|
||||
ValueCell.update(globalUniforms.uSelectStrength, p.selectStrength);
|
||||
}
|
||||
if (props.dimStrength !== undefined && props.dimStrength !== p.dimStrength) {
|
||||
p.dimStrength = props.dimStrength;
|
||||
ValueCell.update(globalUniforms.uDimStrength, p.dimStrength);
|
||||
}
|
||||
if (props.markerPriority !== undefined && props.markerPriority !== p.markerPriority) {
|
||||
p.markerPriority = props.markerPriority;
|
||||
ValueCell.update(globalUniforms.uMarkerPriority, p.markerPriority);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -77,6 +77,7 @@ interface Scene extends Object3D {
|
||||
remove: (o: GraphicsRenderObject) => void
|
||||
commit: (maxTimeMs?: number) => boolean
|
||||
readonly needsCommit: boolean
|
||||
readonly commitQueueSize: number
|
||||
has: (o: GraphicsRenderObject) => boolean
|
||||
clear: () => void
|
||||
forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void
|
||||
@@ -105,6 +106,10 @@ namespace Scene {
|
||||
let boundingSphereDirty = true;
|
||||
let boundingSphereVisibleDirty = true;
|
||||
|
||||
let markerAverageDirty = true;
|
||||
let opacityAverageDirty = true;
|
||||
let hasOpaqueDirty = true;
|
||||
|
||||
let markerAverage = 0;
|
||||
let opacityAverage = 0;
|
||||
let hasOpaque = false;
|
||||
@@ -165,9 +170,9 @@ namespace Scene {
|
||||
}
|
||||
|
||||
renderables.sort(renderableSort);
|
||||
markerAverage = calculateMarkerAverage();
|
||||
opacityAverage = calculateOpacityAverage();
|
||||
hasOpaque = calculateHasOpaque();
|
||||
markerAverageDirty = true;
|
||||
opacityAverageDirty = true;
|
||||
hasOpaqueDirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -189,9 +194,9 @@ namespace Scene {
|
||||
const newVisibleHash = computeVisibleHash();
|
||||
if (newVisibleHash !== visibleHash) {
|
||||
boundingSphereVisibleDirty = true;
|
||||
markerAverage = calculateMarkerAverage();
|
||||
opacityAverage = calculateOpacityAverage();
|
||||
hasOpaque = calculateHasOpaque();
|
||||
markerAverageDirty = true;
|
||||
opacityAverageDirty = true;
|
||||
hasOpaqueDirty = true;
|
||||
visibleHash = newVisibleHash;
|
||||
return true;
|
||||
} else {
|
||||
@@ -268,13 +273,14 @@ namespace Scene {
|
||||
} else {
|
||||
syncVisibility();
|
||||
}
|
||||
markerAverage = calculateMarkerAverage();
|
||||
opacityAverage = calculateOpacityAverage();
|
||||
hasOpaque = calculateHasOpaque();
|
||||
markerAverageDirty = true;
|
||||
opacityAverageDirty = true;
|
||||
hasOpaqueDirty = true;
|
||||
},
|
||||
add: (o: GraphicsRenderObject) => commitQueue.add(o),
|
||||
remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
|
||||
commit: (maxTime = Number.MAX_VALUE) => commit(maxTime),
|
||||
get commitQueueSize() { return commitQueue.size; },
|
||||
get needsCommit() { return !commitQueue.isEmpty; },
|
||||
has: (o: GraphicsRenderObject) => {
|
||||
return renderableMap.has(o);
|
||||
@@ -311,12 +317,24 @@ namespace Scene {
|
||||
return boundingSphereVisible;
|
||||
},
|
||||
get markerAverage() {
|
||||
if (markerAverageDirty) {
|
||||
markerAverage = calculateMarkerAverage();
|
||||
markerAverageDirty = false;
|
||||
}
|
||||
return markerAverage;
|
||||
},
|
||||
get opacityAverage() {
|
||||
if (opacityAverageDirty) {
|
||||
opacityAverage = calculateOpacityAverage();
|
||||
opacityAverageDirty = false;
|
||||
}
|
||||
return opacityAverage;
|
||||
},
|
||||
get hasOpaque() {
|
||||
if (hasOpaqueDirty) {
|
||||
hasOpaque = calculateHasOpaque();
|
||||
hasOpaqueDirty = false;
|
||||
}
|
||||
return hasOpaque;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const apply_marker_color = `
|
||||
|
||||
#if defined(dColorMarker)
|
||||
if (marker > 0.0) {
|
||||
if ((uMarkerPriority == 1 && marker != 2.0) || (uMarkerPriority != 1 && marker == 1.0)) {
|
||||
@@ -8,6 +9,9 @@ export const apply_marker_color = `
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uSelectColor, uSelectStrength);
|
||||
gl_FragColor.a = max(gl_FragColor.a, uSelectStrength * 0.002); // for direct-volume rendering
|
||||
}
|
||||
} else if (uMarkerAverage > 0.0) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uDimColor, uDimStrength);
|
||||
gl_FragColor.a = max(gl_FragColor.a, uDimStrength * 0.002); // for direct-volume rendering
|
||||
}
|
||||
#endif
|
||||
`;
|
||||
@@ -25,9 +25,12 @@ uniform int uMarkingType;
|
||||
#if defined(dColorMarker)
|
||||
uniform vec3 uHighlightColor;
|
||||
uniform vec3 uSelectColor;
|
||||
uniform vec3 uDimColor;
|
||||
uniform float uHighlightStrength;
|
||||
uniform float uSelectStrength;
|
||||
uniform float uDimStrength;
|
||||
uniform int uMarkerPriority;
|
||||
uniform float uMarkerAverage;
|
||||
#endif
|
||||
|
||||
#if defined(dNeedsMarker)
|
||||
|
||||
@@ -53,9 +53,12 @@ uniform int uGroupCount;
|
||||
#if defined(dColorMarker)
|
||||
uniform vec3 uHighlightColor;
|
||||
uniform vec3 uSelectColor;
|
||||
uniform vec3 uDimColor;
|
||||
uniform float uHighlightStrength;
|
||||
uniform float uSelectStrength;
|
||||
uniform float uDimStrength;
|
||||
uniform int uMarkerPriority;
|
||||
uniform float uMarkerAverage;
|
||||
|
||||
uniform float uMarker;
|
||||
uniform vec2 uMarkerTexDim;
|
||||
|
||||
@@ -6,6 +6,8 @@ uniform vec2 uTexSizeInv;
|
||||
uniform sampler2D tEdgeTexture;
|
||||
uniform vec3 uHighlightEdgeColor;
|
||||
uniform vec3 uSelectEdgeColor;
|
||||
uniform float uHighlightEdgeStrength;
|
||||
uniform float uSelectEdgeStrength;
|
||||
uniform float uGhostEdgeStrength;
|
||||
uniform float uInnerEdgeFactor;
|
||||
|
||||
@@ -16,6 +18,8 @@ void main() {
|
||||
vec3 edgeColor = edgeValue.b == 1.0 ? uHighlightEdgeColor : uSelectEdgeColor;
|
||||
gl_FragColor.rgb = edgeValue.g > 0.0 ? edgeColor : edgeColor * uInnerEdgeFactor;
|
||||
gl_FragColor.a = (edgeValue.r == 1.0 ? uGhostEdgeStrength : 1.0) * edgeValue.a;
|
||||
float edgeStrength = edgeValue.b == 1.0 ? uHighlightEdgeStrength : uSelectEdgeStrength;
|
||||
gl_FragColor.a *= edgeStrength;
|
||||
} else {
|
||||
gl_FragColor = vec4(0.0);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
export const outlines_frag = `
|
||||
@@ -63,6 +63,7 @@ void main(void) {
|
||||
|
||||
float outline = 1.0;
|
||||
float bestDepth = 1.0;
|
||||
float transparentFlag = 0.0;
|
||||
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
@@ -82,11 +83,12 @@ void main(void) {
|
||||
if (abs(selfViewZTransparent - sampleViewZTransparent) > uMaxPossibleViewZDiff && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) {
|
||||
outline = 0.0;
|
||||
bestDepth = sampleDepthTransparent;
|
||||
transparentFlag = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(outline, packUnitIntervalToRG(bestDepth), 0.0);
|
||||
gl_FragColor = vec4(outline, packUnitIntervalToRG(bestDepth), transparentFlag);
|
||||
}
|
||||
`;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -29,7 +29,9 @@ uniform bool uTransparentBackground;
|
||||
uniform vec2 uOcclusionOffset;
|
||||
|
||||
uniform float uMaxPossibleViewZDiff;
|
||||
uniform mat4 uInvProjection;
|
||||
|
||||
const float outlineDistanceFactor = 5.0;
|
||||
const vec3 occlusionColor = vec3(0.0);
|
||||
|
||||
#include common
|
||||
@@ -62,12 +64,21 @@ bool isBackground(const in float depth) {
|
||||
return depth == 1.0;
|
||||
}
|
||||
|
||||
float getPixelSize(const in vec2 coords, const in float depth) {
|
||||
vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);
|
||||
vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);
|
||||
return distance(viewPos0, viewPos1);
|
||||
}
|
||||
|
||||
float getOutline(const in vec2 coords, const in float opaqueDepth, out float closestTexel) {
|
||||
float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
|
||||
vec2 invTexSize = 1.0 / uTexSize;
|
||||
|
||||
float selfDepth = min(opaqueDepth, getDepthTransparent(coords));
|
||||
float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(selfDepth);
|
||||
float transparentDepth = getDepthTransparent(coords);
|
||||
float opaqueSelfViewZ = isBackground(opaqueDepth) ? backgroundViewZ : getViewZ(opaqueDepth);
|
||||
float transparentSelfViewZ = isBackground(transparentDepth) ? backgroundViewZ : getViewZ(transparentDepth);
|
||||
float selfDepth = min(opaqueDepth, transparentDepth);
|
||||
float pixelSize = getPixelSize(coords, selfDepth);
|
||||
|
||||
float outline = 1.0;
|
||||
closestTexel = 1.0;
|
||||
@@ -84,7 +95,8 @@ float getOutline(const in vec2 coords, const in float opaqueDepth, out float clo
|
||||
float sampleOutlineDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb);
|
||||
float sampleOutlineViewZ = isBackground(sampleOutlineDepth) ? backgroundViewZ : getViewZ(sampleOutlineDepth);
|
||||
|
||||
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff) {
|
||||
float selfViewZ = sampleOutlineCombined.a == 0.0 ? opaqueSelfViewZ : transparentSelfViewZ;
|
||||
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff + (pixelSize * outlineDistanceFactor)) {
|
||||
outline = 0.0;
|
||||
closestTexel = sampleOutlineDepth;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -115,7 +115,7 @@ void main(void) {
|
||||
}
|
||||
occlusion = 1.0 - (uBias * occlusion / float(dNSamples));
|
||||
|
||||
vec2 packedOcclusion = packUnitIntervalToRG(occlusion);
|
||||
vec2 packedOcclusion = packUnitIntervalToRG(clamp(occlusion, 0.01, 1.0));
|
||||
|
||||
gl_FragColor = vec4(packedOcclusion, selfPackedDepth);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -29,7 +29,7 @@ uniform float uOffsetX;
|
||||
uniform float uOffsetY;
|
||||
uniform float uOffsetZ;
|
||||
|
||||
// uniform bool ortho;
|
||||
uniform float uIsOrtho;
|
||||
uniform float uPixelRatio;
|
||||
uniform vec4 uViewport;
|
||||
|
||||
@@ -77,13 +77,11 @@ void main(void){
|
||||
mvCorner.x += offsetX;
|
||||
mvCorner.y += offsetY;
|
||||
|
||||
// TODO
|
||||
// if(ortho){
|
||||
// mvCorner.xyz += normalize(-uCameraPosition) * offsetZ;
|
||||
// } else {
|
||||
// mvCorner.xyz += normalize(-mvCorner.xyz) * offsetZ;
|
||||
// }
|
||||
mvCorner.xyz += normalize(-mvCorner.xyz) * offsetZ;
|
||||
if (uIsOrtho == 1.0) {
|
||||
mvCorner.z += offsetZ;
|
||||
} else {
|
||||
mvCorner.xyz += normalize(-mvCorner.xyz) * offsetZ;
|
||||
}
|
||||
|
||||
gl_Position = uProjection * mvCorner;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -523,6 +523,31 @@ export function getFboRenderMipmap(gl: GLRenderingContext): COMPAT_fboRenderMipm
|
||||
return isWebGL2(gl) ? {} : gl.getExtension('OES_fbo_render_mipmap');
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://registry.khronos.org/webgl/extensions/WEBGL_provoking_vertex/
|
||||
*/
|
||||
export interface COMPAT_provoking_vertex {
|
||||
readonly FIRST_VERTEX_CONVENTION: number;
|
||||
readonly LAST_VERTEX_CONVENTION: number;
|
||||
readonly PROVOKING_VERTEX: number;
|
||||
provokingVertex(provokeMode: number): void;
|
||||
}
|
||||
|
||||
export function getProvokingVertex(gl: GLRenderingContext): COMPAT_provoking_vertex | null {
|
||||
if (isWebGL2(gl)) {
|
||||
const ext = gl.getExtension('WEBGL_provoking_vertex');
|
||||
if (ext) {
|
||||
return {
|
||||
FIRST_VERTEX_CONVENTION: ext.FIRST_VERTEX_CONVENTION_WEBGL,
|
||||
LAST_VERTEX_CONVENTION: ext.LAST_VERTEX_CONVENTION_WEBGL,
|
||||
PROVOKING_VERTEX: ext.PROVOKING_VERTEX_WEBGL,
|
||||
provokingVertex: ext.provokingVertexWEBGL.bind(ext)
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getNoNonInstancedActiveAttribs(gl: GLRenderingContext): boolean {
|
||||
if (!isWebGL2(gl)) return false;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -155,7 +155,7 @@ function getDrawingBufferPixelData(gl: GLRenderingContext, state: WebGLState) {
|
||||
//
|
||||
|
||||
function createStats() {
|
||||
return {
|
||||
const stats = {
|
||||
resourceCounts: {
|
||||
attribute: 0,
|
||||
elements: 0,
|
||||
@@ -171,7 +171,13 @@ function createStats() {
|
||||
drawCount: 0,
|
||||
instanceCount: 0,
|
||||
instancedDrawCount: 0,
|
||||
|
||||
calls: {
|
||||
drawInstanced: 0,
|
||||
counts: 0,
|
||||
},
|
||||
};
|
||||
return stats;
|
||||
}
|
||||
|
||||
export type WebGLStats = ReturnType<typeof createStats>
|
||||
@@ -208,7 +214,7 @@ export interface WebGLContext {
|
||||
/** Cache for textures, managed by consumers */
|
||||
readonly namedTextures: { [name: string]: Texture }
|
||||
|
||||
createRenderTarget: (width: number, height: number, depth?: boolean, type?: 'uint8' | 'float32' | 'fp16', filter?: TextureFilter) => RenderTarget
|
||||
createRenderTarget: (width: number, height: number, depth?: boolean, type?: 'uint8' | 'float32' | 'fp16', filter?: TextureFilter, format?: 'rgba' | 'alpha') => RenderTarget
|
||||
unbindFramebuffer: () => void
|
||||
readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array | Int32Array) => void
|
||||
readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void>
|
||||
@@ -224,7 +230,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
|
||||
const state = createState(gl);
|
||||
const stats = createStats();
|
||||
const resources = createResources(gl, state, stats, extensions);
|
||||
const timer = createTimer(gl, extensions);
|
||||
const timer = createTimer(gl, extensions, stats);
|
||||
|
||||
const parameters = {
|
||||
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,
|
||||
@@ -239,6 +245,11 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
|
||||
throw new Error('Need "MAX_VERTEX_TEXTURE_IMAGE_UNITS" >= 8');
|
||||
}
|
||||
|
||||
// optimize assuming flats first and last data are same or differences don't matter
|
||||
// extension is only available when `FIRST_VERTEX_CONVENTION` is more efficient
|
||||
const epv = extensions.provokingVertex;
|
||||
epv?.provokingVertex(epv.FIRST_VERTEX_CONVENTION);
|
||||
|
||||
let isContextLost = false;
|
||||
const contextRestored = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp);
|
||||
|
||||
@@ -328,8 +339,8 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
|
||||
contextRestored.next(now());
|
||||
},
|
||||
|
||||
createRenderTarget: (width: number, height: number, depth?: boolean, type?: 'uint8' | 'float32' | 'fp16', filter?: TextureFilter) => {
|
||||
const renderTarget = createRenderTarget(gl, resources, width, height, depth, type, filter);
|
||||
createRenderTarget: (width: number, height: number, depth?: boolean, type?: 'uint8' | 'float32' | 'fp16', filter?: TextureFilter, format?: 'rgba' | 'alpha') => {
|
||||
const renderTarget = createRenderTarget(gl, resources, width, height, depth, type, filter, format);
|
||||
renderTargets.add(renderTarget);
|
||||
return {
|
||||
...renderTarget,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat, getVertexArrayObject, getDisjointTimerQuery, COMPAT_disjoint_timer_query, getNoNonInstancedActiveAttribs, getDrawBuffersIndexed, COMPAT_draw_buffers_indexed, getParallelShaderCompile, COMPAT_parallel_shader_compile, getFboRenderMipmap, COMPAT_fboRenderMipmap } from './compat';
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat, getVertexArrayObject, getDisjointTimerQuery, COMPAT_disjoint_timer_query, getNoNonInstancedActiveAttribs, getDrawBuffersIndexed, COMPAT_draw_buffers_indexed, getParallelShaderCompile, COMPAT_parallel_shader_compile, getFboRenderMipmap, COMPAT_fboRenderMipmap, COMPAT_provoking_vertex, getProvokingVertex } from './compat';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
|
||||
export type WebGLExtensions = {
|
||||
@@ -29,6 +29,7 @@ export type WebGLExtensions = {
|
||||
disjointTimerQuery: COMPAT_disjoint_timer_query | null
|
||||
parallelShaderCompile: COMPAT_parallel_shader_compile | null
|
||||
fboRenderMipmap: COMPAT_fboRenderMipmap | null
|
||||
provokingVertex: COMPAT_provoking_vertex | null
|
||||
|
||||
noNonInstancedActiveAttribs: boolean
|
||||
}
|
||||
@@ -121,6 +122,10 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
|
||||
if (isDebugMode && fboRenderMipmap === null) {
|
||||
console.log('Could not find support for "fbo_render_mipmap"');
|
||||
}
|
||||
const provokingVertex = getProvokingVertex(gl);
|
||||
if (isDebugMode && provokingVertex === null) {
|
||||
console.log('Could not find support for "provoking_vertex"');
|
||||
}
|
||||
|
||||
const noNonInstancedActiveAttribs = getNoNonInstancedActiveAttribs(gl);
|
||||
|
||||
@@ -146,6 +151,7 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
|
||||
disjointTimerQuery,
|
||||
parallelShaderCompile,
|
||||
fboRenderMipmap,
|
||||
provokingVertex,
|
||||
|
||||
noNonInstancedActiveAttribs,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
@@ -15,7 +15,7 @@ import { idFactory } from '../../mol-util/id-factory';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { TextureImage, TextureVolume } from '../../mol-gl/renderable/util';
|
||||
import { checkFramebufferStatus } from './framebuffer';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
|
||||
import { VertexArray } from './vertex-array';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { deepClone } from '../../mol-util/object';
|
||||
@@ -212,6 +212,10 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
|
||||
} else {
|
||||
instancedArrays.drawArraysInstanced(glDrawMode, 0, drawCount, instanceCount);
|
||||
}
|
||||
if (isTimingMode) {
|
||||
stats.calls.drawInstanced += 1;
|
||||
stats.calls.counts += instanceCount;
|
||||
}
|
||||
if (isDebugMode) {
|
||||
try {
|
||||
checkError(ctx.gl);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -28,14 +28,18 @@ export interface RenderTarget {
|
||||
destroy: () => void
|
||||
}
|
||||
|
||||
export function createRenderTarget(gl: GLRenderingContext, resources: WebGLResources, _width: number, _height: number, depth = true, type: 'uint8' | 'float32' | 'fp16' = 'uint8', filter: TextureFilter = 'nearest'): RenderTarget {
|
||||
export function createRenderTarget(gl: GLRenderingContext, resources: WebGLResources, _width: number, _height: number, depth = true, type: 'uint8' | 'float32' | 'fp16' = 'uint8', filter: TextureFilter = 'nearest', format: 'rgba' | 'alpha' = 'rgba'): RenderTarget {
|
||||
|
||||
if (format === 'alpha' && !isWebGL2(gl)) {
|
||||
throw new Error('cannot render to alpha format in webgl1');
|
||||
}
|
||||
|
||||
const framebuffer = resources.framebuffer();
|
||||
const targetTexture = type === 'fp16'
|
||||
? resources.texture('image-float16', 'rgba', 'fp16', filter)
|
||||
? resources.texture('image-float16', format, 'fp16', filter)
|
||||
: type === 'float32'
|
||||
? resources.texture('image-float32', 'rgba', 'float', filter)
|
||||
: resources.texture('image-uint8', 'rgba', 'ubyte', filter);
|
||||
? resources.texture('image-float32', format, 'float', filter)
|
||||
: resources.texture('image-uint8', format, 'ubyte', filter);
|
||||
// make a depth renderbuffer of the same size as the targetTexture
|
||||
const depthRenderbuffer = !depth
|
||||
? null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
@@ -554,12 +554,18 @@ export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipm
|
||||
|
||||
//
|
||||
|
||||
const NullTextureFormat = -1;
|
||||
|
||||
export function isNullTexture(texture: Texture) {
|
||||
return texture.format === NullTextureFormat;
|
||||
}
|
||||
|
||||
export function createNullTexture(gl?: GLRenderingContext): Texture {
|
||||
const target = gl?.TEXTURE_2D ?? 3553;
|
||||
return {
|
||||
id: getNextTextureId(),
|
||||
target,
|
||||
format: 0,
|
||||
format: NullTextureFormat,
|
||||
internalFormat: 0,
|
||||
type: 0,
|
||||
filter: 0,
|
||||
@@ -583,8 +589,12 @@ export function createNullTexture(gl?: GLRenderingContext): Texture {
|
||||
gl.bindTexture(target, null);
|
||||
}
|
||||
},
|
||||
attachFramebuffer: () => {},
|
||||
detachFramebuffer: () => {},
|
||||
attachFramebuffer: () => {
|
||||
throw new Error('cannot attach null-texture to a framebuffer');
|
||||
},
|
||||
detachFramebuffer: () => {
|
||||
throw new Error('cannot detach null-texture from a framebuffer');
|
||||
},
|
||||
|
||||
reset: () => {},
|
||||
destroy: () => {},
|
||||
|
||||
@@ -1,16 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2022-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { now } from '../../mol-util/now';
|
||||
import { GLRenderingContext } from './compat';
|
||||
import { WebGLStats } from './context';
|
||||
import { WebGLExtensions } from './extensions';
|
||||
|
||||
function movingAverage(avg: number, sample: number, count: number) {
|
||||
avg -= avg / count;
|
||||
avg += sample / count;
|
||||
return avg;
|
||||
}
|
||||
|
||||
class MovingAverage {
|
||||
private readonly avgs = new Map<string, number>();
|
||||
|
||||
add(label: string, sample: number) {
|
||||
let avg = this.avgs.get(label) || sample;
|
||||
avg = movingAverage(avg, sample, this.count);
|
||||
this.avgs.set(label, avg);
|
||||
return avg;
|
||||
}
|
||||
|
||||
get(label: string) {
|
||||
return this.avgs.get(label);
|
||||
}
|
||||
|
||||
stats() {
|
||||
return Object.fromEntries(this.avgs.entries());
|
||||
}
|
||||
|
||||
constructor(private count: number) { }
|
||||
}
|
||||
|
||||
function clearStatsCalls(stats: WebGLStats) {
|
||||
stats.calls.drawInstanced = 0;
|
||||
stats.calls.counts = 0;
|
||||
}
|
||||
|
||||
export type TimerResult = {
|
||||
readonly label: string
|
||||
readonly timeElapsed: number
|
||||
readonly gpuElapsed: number
|
||||
readonly gpuAvg: number
|
||||
readonly cpuElapsed: number
|
||||
readonly cpuAvg: number
|
||||
readonly children: TimerResult[]
|
||||
readonly calls?: Calls
|
||||
}
|
||||
|
||||
function getQuery(extensions: WebGLExtensions) {
|
||||
@@ -20,24 +58,45 @@ function getQuery(extensions: WebGLExtensions) {
|
||||
export type WebGLTimer = {
|
||||
/** Check with GPU for finished timers. */
|
||||
resolve: () => TimerResult[]
|
||||
mark: (label: string) => void
|
||||
mark: (label: string, captureCalls?: boolean) => void
|
||||
markEnd: (label: string) => void
|
||||
stats: () => { gpu: Record<string, number>, cpu: Record<string, number> }
|
||||
formatedStats: () => Record<string, string>
|
||||
clear: () => void
|
||||
destroy: () => void
|
||||
}
|
||||
|
||||
type Measure = { label: string, queries: WebGLQuery[], children: Measure[], root: boolean, timeElapsed?: number };
|
||||
type Calls = {
|
||||
drawInstanced: number,
|
||||
counts: number,
|
||||
}
|
||||
|
||||
type Measure = {
|
||||
label: string,
|
||||
queries: WebGLQuery[],
|
||||
children: Measure[],
|
||||
root: boolean,
|
||||
cpu: { start: number, end: number },
|
||||
captureCalls: boolean,
|
||||
timeElapsed?: number,
|
||||
calls?: Calls,
|
||||
};
|
||||
|
||||
type QueryResult = { timeElapsed?: number, refCount: number };
|
||||
|
||||
export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions): WebGLTimer {
|
||||
export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions, stats: WebGLStats, options?: { avgCount: number }): WebGLTimer {
|
||||
const dtq = extensions.disjointTimerQuery;
|
||||
const avgCount = options?.avgCount ?? 30;
|
||||
|
||||
const queries = new Map<WebGLQuery, QueryResult>();
|
||||
const pending = new Map<string, Measure>();
|
||||
const stack: Measure[] = [];
|
||||
const gpuAvgs = new MovingAverage(avgCount);
|
||||
const cpuAvgs = new MovingAverage(avgCount);
|
||||
|
||||
let measures: Measure[] = [];
|
||||
let current: WebGLQuery | null = null;
|
||||
let capturingCalls = false;
|
||||
|
||||
const clear = () => {
|
||||
if (!dtq) return;
|
||||
@@ -100,17 +159,32 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
const children: TimerResult[] = [];
|
||||
const add = (measures: Measure[], children: TimerResult[]) => {
|
||||
for (const measure of measures) {
|
||||
const timeElapsed = measure.timeElapsed!;
|
||||
const cpuElapsed = measure.cpu.end - measure.cpu.start;
|
||||
const result: TimerResult = {
|
||||
label: measure.label,
|
||||
timeElapsed: measure.timeElapsed!,
|
||||
children: []
|
||||
gpuElapsed: timeElapsed,
|
||||
gpuAvg: gpuAvgs.add(measure.label, timeElapsed),
|
||||
cpuElapsed,
|
||||
cpuAvg: cpuAvgs.add(measure.label, cpuElapsed),
|
||||
children: [],
|
||||
calls: measure.calls,
|
||||
};
|
||||
children.push(result);
|
||||
add(measure.children, result.children);
|
||||
}
|
||||
};
|
||||
add(measure.children, children);
|
||||
results.push({ label: measure.label, timeElapsed, children });
|
||||
const cpuElapsed = measure.cpu.end - measure.cpu.start;
|
||||
results.push({
|
||||
label: measure.label,
|
||||
gpuElapsed: timeElapsed,
|
||||
gpuAvg: gpuAvgs.add(measure.label, timeElapsed),
|
||||
cpuElapsed,
|
||||
cpuAvg: cpuAvgs.add(measure.label, cpuElapsed),
|
||||
children,
|
||||
calls: measure.calls,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
unresolved.push(measure);
|
||||
@@ -126,7 +200,7 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
|
||||
return results;
|
||||
},
|
||||
mark: (label: string) => {
|
||||
mark: (label: string, captureCalls = false) => {
|
||||
if (!dtq) return;
|
||||
|
||||
if (pending.has(label)) {
|
||||
@@ -136,7 +210,14 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
if (current !== null) {
|
||||
dtq.endQuery(dtq.TIME_ELAPSED);
|
||||
}
|
||||
const measure: Measure = { label, queries: [], children: [], root: current === null };
|
||||
const measure: Measure = {
|
||||
label,
|
||||
queries: [],
|
||||
children: [],
|
||||
root: current === null,
|
||||
cpu: { start: now(), end: -1 },
|
||||
captureCalls,
|
||||
};
|
||||
pending.set(label, measure);
|
||||
|
||||
if (stack.length) {
|
||||
@@ -144,6 +225,14 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
}
|
||||
stack.push(measure);
|
||||
|
||||
if (captureCalls) {
|
||||
if (capturingCalls) {
|
||||
throw new Error('Already capturing calls');
|
||||
}
|
||||
clearStatsCalls(stats);
|
||||
capturingCalls = true;
|
||||
}
|
||||
|
||||
add();
|
||||
},
|
||||
markEnd: (label: string) => {
|
||||
@@ -160,6 +249,16 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
|
||||
dtq.endQuery(dtq.TIME_ELAPSED);
|
||||
pending.delete(label);
|
||||
|
||||
measure.cpu.end = now();
|
||||
if (measure.captureCalls) {
|
||||
measure.calls = {
|
||||
drawInstanced: stats.calls.drawInstanced,
|
||||
counts: stats.calls.counts,
|
||||
};
|
||||
capturingCalls = false;
|
||||
}
|
||||
|
||||
measures.push(measure);
|
||||
|
||||
if (pending.size > 0) {
|
||||
@@ -168,6 +267,23 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
current = null;
|
||||
}
|
||||
},
|
||||
stats: () => {
|
||||
return {
|
||||
gpu: gpuAvgs.stats(),
|
||||
cpu: cpuAvgs.stats(),
|
||||
};
|
||||
},
|
||||
formatedStats: () => {
|
||||
const stats: Record<string, string> = {};
|
||||
const gpu = gpuAvgs.stats();
|
||||
const cpu = cpuAvgs.stats();
|
||||
for (const l of Object.keys(gpu)) {
|
||||
const g = `${(gpu[l] / 1000 / 1000).toFixed(2)}`;
|
||||
const c = `${cpu[l].toFixed(2)}`;
|
||||
stats[l] = `${g} ms | CPU: ${c} ms`;
|
||||
}
|
||||
return stats;
|
||||
},
|
||||
clear,
|
||||
destroy: () => {
|
||||
clear();
|
||||
@@ -176,15 +292,19 @@ export function createTimer(gl: GLRenderingContext, extensions: WebGLExtensions)
|
||||
}
|
||||
|
||||
function formatTimerResult(result: TimerResult) {
|
||||
const timeElapsed = result.timeElapsed / 1000 / 1000;
|
||||
return `${result.label} ${timeElapsed.toFixed(2)}ms`;
|
||||
const gpu = `${(result.gpuElapsed / 1000 / 1000).toFixed(2)}`;
|
||||
const gpuAvg = `${(result.gpuAvg / 1000 / 1000).toFixed(2)}`;
|
||||
const cpu = `${result.cpuElapsed.toFixed(2)}`;
|
||||
const cpuAvg = `${result.cpuAvg.toFixed(2)}`;
|
||||
return `${result.label} ${gpu} ms (avg. ${gpuAvg} ms) | CPU: ${cpu} ms (avg. ${cpuAvg} ms)`;
|
||||
}
|
||||
|
||||
export function printTimerResults(results: TimerResult[]) {
|
||||
return results.map(r => {
|
||||
results.map(r => {
|
||||
const f = formatTimerResult(r);
|
||||
if (r.children.length) {
|
||||
if (r.children.length || r.calls) {
|
||||
console.groupCollapsed(f);
|
||||
if (r.calls) console.log(r.calls);
|
||||
printTimerResults(r.children);
|
||||
console.groupEnd();
|
||||
} else {
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface EncodedData {
|
||||
|
||||
export namespace Encoding {
|
||||
|
||||
export const enum IntDataType {
|
||||
export enum IntDataType {
|
||||
Int8 = 1,
|
||||
Int16 = 2,
|
||||
Int32 = 3,
|
||||
@@ -66,7 +66,7 @@ export namespace Encoding {
|
||||
Uint32 = 6,
|
||||
}
|
||||
|
||||
export const enum FloatDataType {
|
||||
export enum FloatDataType {
|
||||
Float32 = 32,
|
||||
Float64 = 33
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { parseFloat as fastParseFloat, parseInt as fastParseInt, getNumberType, NumberType } from '../../../mol-io/reader/common/text/number-parser';
|
||||
import { parseFloat as fastParseFloat, parseInt as fastParseInt, getNumberType, NumberTypes } from '../../../mol-io/reader/common/text/number-parser';
|
||||
|
||||
describe('common', () => {
|
||||
it('number-parser fastParseFloat', () => {
|
||||
@@ -24,17 +24,17 @@ describe('common', () => {
|
||||
});
|
||||
|
||||
it('number-parser getNumberType', () => {
|
||||
expect(getNumberType('11')).toBe(NumberType.Int);
|
||||
expect(getNumberType('5E93')).toBe(NumberType.Scientific);
|
||||
expect(getNumberType('0.42')).toBe(NumberType.Float);
|
||||
expect(getNumberType('Foo123')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('11.0829(23)')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('1..2')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('.')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('-.')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('e')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('-e')).toBe(NumberType.NaN);
|
||||
expect(getNumberType('1e')).toBe(NumberType.Scientific);
|
||||
expect(getNumberType('-1e')).toBe(NumberType.Scientific);
|
||||
expect(getNumberType('11')).toBe(NumberTypes.Int);
|
||||
expect(getNumberType('5E93')).toBe(NumberTypes.Scientific);
|
||||
expect(getNumberType('0.42')).toBe(NumberTypes.Float);
|
||||
expect(getNumberType('Foo123')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('11.0829(23)')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('1..2')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('.')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('-.')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('e')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('-e')).toBe(NumberTypes.NaN);
|
||||
expect(getNumberType('1e')).toBe(NumberTypes.Scientific);
|
||||
expect(getNumberType('-1e')).toBe(NumberTypes.Scientific);
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,7 @@ import { CifCore_Database, CifCore_Schema, CifCore_Aliases } from './cif/schema/
|
||||
import { Segmentation_Data_Database, Segmentation_Data_Schema } from './cif/schema/segmentation';
|
||||
|
||||
export const CIF = {
|
||||
parse: (data: string|Uint8Array) => typeof data === 'string' ? parseCifText(data) : parseCifBinary(data),
|
||||
parse: (data: string | Uint8Array) => typeof data === 'string' ? parseCifText(data) : parseCifBinary(data),
|
||||
parseText: parseCifText,
|
||||
parseBinary: parseCifBinary,
|
||||
toDatabaseCollection,
|
||||
|
||||
@@ -17,10 +17,10 @@ export function Field(column: EncodedColumn): Data.CifField {
|
||||
|
||||
const str: Data.CifField['str'] = isNumeric
|
||||
? mask
|
||||
? row => mask[row] === Column.ValueKind.Present ? '' + data[row] : ''
|
||||
? row => mask[row] === Column.ValueKinds.Present ? '' + data[row] : ''
|
||||
: row => '' + data[row]
|
||||
: mask
|
||||
? row => mask[row] === Column.ValueKind.Present ? data[row] : ''
|
||||
? row => mask[row] === Column.ValueKinds.Present ? data[row] : ''
|
||||
: row => data[row];
|
||||
|
||||
const int: Data.CifField['int'] = isNumeric
|
||||
@@ -32,8 +32,8 @@ export function Field(column: EncodedColumn): Data.CifField {
|
||||
: row => { const v = data[row]; return fastParseFloat(v, 0, v.length); };
|
||||
|
||||
const valueKind: Data.CifField['valueKind'] = mask
|
||||
? row => mask[row]
|
||||
: row => Column.ValueKind.Present;
|
||||
? row => mask[row] as Column.ValueKind
|
||||
: row => Column.ValueKinds.Present;
|
||||
|
||||
const rowCount = data.length;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { Column, ColumnHelpers, Table } from '../../../mol-data/db';
|
||||
import { Tensor } from '../../../mol-math/linear-algebra';
|
||||
import { getNumberType, NumberType, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser';
|
||||
import { getNumberType, NumberTypes, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser';
|
||||
import { Encoding } from '../../common/binary-cif';
|
||||
import { Tokens } from '../common/text/tokenizer';
|
||||
import { areValuesEqualProvider } from '../common/text/column/token';
|
||||
@@ -124,12 +124,12 @@ export namespace CifField {
|
||||
const float: CifField['float'] = row => { const v = values[row]; return fastParseFloat(v, 0, v.length) || 0; };
|
||||
const valueKind: CifField['valueKind'] = row => {
|
||||
const v = values[row], l = v.length;
|
||||
if (l > 1) return Column.ValueKind.Present;
|
||||
if (l === 0) return Column.ValueKind.NotPresent;
|
||||
if (l > 1) return Column.ValueKinds.Present;
|
||||
if (l === 0) return Column.ValueKinds.NotPresent;
|
||||
const c = v.charCodeAt(0);
|
||||
if (c === 46 /* . */) return Column.ValueKind.NotPresent;
|
||||
if (c === 63 /* ? */) return Column.ValueKind.Unknown;
|
||||
return Column.ValueKind.Present;
|
||||
if (c === 46 /* . */) return Column.ValueKinds.NotPresent;
|
||||
if (c === 63 /* ? */) return Column.ValueKinds.Unknown;
|
||||
return Column.ValueKinds.Present;
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -152,7 +152,7 @@ export namespace CifField {
|
||||
const rowCount = values.length;
|
||||
const str: CifField['str'] = row => { return '' + values[row]; };
|
||||
const float: CifField['float'] = row => values[row];
|
||||
const valueKind: CifField['valueKind'] = row => Column.ValueKind.Present;
|
||||
const valueKind: CifField['valueKind'] = row => Column.ValueKinds.Present;
|
||||
|
||||
const toFloatArray = (params: Column.ToArrayParams<number>) => {
|
||||
if (!params || params.array && values instanceof params.array) {
|
||||
@@ -197,12 +197,12 @@ export namespace CifField {
|
||||
|
||||
const valueKind: CifField['valueKind'] = row => {
|
||||
const s = indices[2 * row], l = indices[2 * row + 1] - s;
|
||||
if (l > 1) return Column.ValueKind.Present;
|
||||
if (l === 0) return Column.ValueKind.NotPresent;
|
||||
if (l > 1) return Column.ValueKinds.Present;
|
||||
if (l === 0) return Column.ValueKinds.NotPresent;
|
||||
const v = data.charCodeAt(s);
|
||||
if (v === 46 /* . */) return Column.ValueKind.NotPresent;
|
||||
if (v === 63 /* ? */) return Column.ValueKind.Unknown;
|
||||
return Column.ValueKind.Present;
|
||||
if (v === 46 /* . */) return Column.ValueKinds.NotPresent;
|
||||
if (v === 63 /* ? */) return Column.ValueKinds.Unknown;
|
||||
return Column.ValueKinds.Present;
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -328,13 +328,13 @@ export function getCifFieldType(field: CifField): Column.Schema.Int | Column.Sch
|
||||
let floatCount = 0, hasStringOrScientific = false, undefinedCount = 0;
|
||||
for (let i = 0, _i = field.rowCount; i < _i; i++) {
|
||||
const k = field.valueKind(i);
|
||||
if (k !== Column.ValueKind.Present) {
|
||||
if (k !== Column.ValueKinds.Present) {
|
||||
undefinedCount++;
|
||||
continue;
|
||||
}
|
||||
const type = getNumberType(field.str(i));
|
||||
if (type === NumberType.Int) continue;
|
||||
else if (type === NumberType.Float) floatCount++;
|
||||
if (type === NumberTypes.Int) continue;
|
||||
else if (type === NumberTypes.Float) floatCount++;
|
||||
else { hasStringOrScientific = true; break; }
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ function createListColumn<T extends number | string>(schema: Column.Schema.List<
|
||||
isDefined: !!f,
|
||||
rowCount: category.rowCount,
|
||||
value,
|
||||
valueKind: f ? f.valueKind : () => Column.ValueKind.NotPresent,
|
||||
valueKind: f ? f.valueKind : () => Column.ValueKinds.NotPresent,
|
||||
areValuesEqual: (rowA, rowB) => arrayEqual(value(rowA), value(rowB)),
|
||||
toArray
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.365, IHM 1.18, MA 1.4.4.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.365, IHM 1.18, MA 1.4.4.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.365, IHM 1.18, MA 1.4.4.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
@@ -3470,7 +3470,7 @@ export const mmCIF_Schema = {
|
||||
/**
|
||||
* The clustering method used to obtain the ensemble, if applicable.
|
||||
*/
|
||||
ensemble_clustering_method: Aliased<'Hierarchical' | 'Partitioning (k-means)' | 'Other'>(str),
|
||||
ensemble_clustering_method: Aliased<'Hierarchical' | 'Partitioning (k-means)' | 'Density based threshold-clustering' | 'Other'>(str),
|
||||
/**
|
||||
* The parameter/feature used for clustering the models, if applicable.
|
||||
*/
|
||||
@@ -3628,7 +3628,7 @@ export const mmCIF_Schema = {
|
||||
/**
|
||||
* The type of data held in the dataset.
|
||||
*/
|
||||
data_type: Aliased<'NMR data' | '3DEM volume' | '2DEM class average' | 'EM raw micrographs' | 'X-ray diffraction data' | 'SAS data' | 'CX-MS data' | 'Mass Spectrometry data' | 'EPR data' | 'H/D exchange data' | 'Single molecule FRET data' | 'Experimental model' | 'Comparative model' | 'Integrative model' | 'De Novo model' | 'Predicted contacts' | 'Mutagenesis data' | 'DNA footprinting data' | 'Hydroxyl radical footprinting data' | 'Yeast two-hybrid screening data' | 'Quantitative measurements of genetic interactions' | 'Other'>(str),
|
||||
data_type: Aliased<'NMR data' | '3DEM volume' | '2DEM class average' | 'EM raw micrographs' | 'X-ray diffraction data' | 'SAS data' | 'CX-MS data' | 'Mass Spectrometry data' | 'EPR data' | 'H/D exchange data' | 'Single molecule FRET data' | 'Ensemble FRET data' | 'Experimental model' | 'Comparative model' | 'Integrative model' | 'De Novo model' | 'Predicted contacts' | 'Mutagenesis data' | 'DNA footprinting data' | 'Hydroxyl radical footprinting data' | 'Yeast two-hybrid screening data' | 'Quantitative measurements of genetic interactions' | 'Other'>(str),
|
||||
/**
|
||||
* A flag that indicates whether the dataset is archived in
|
||||
* an IHM related database or elsewhere.
|
||||
|
||||
@@ -39,7 +39,7 @@ export function FixedColumn<T extends Column.Schema>(lines: Tokens, offset: numb
|
||||
isDefined: true,
|
||||
rowCount,
|
||||
value,
|
||||
valueKind: row => Column.ValueKind.Present,
|
||||
valueKind: row => Column.ValueKinds.Present,
|
||||
toArray: params => ColumnHelpers.createAndFillArray(rowCount, value, params),
|
||||
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ export function TokenColumn<T extends Column.Schema>(tokens: Tokens, schema: T):
|
||||
isDefined: true,
|
||||
rowCount,
|
||||
value,
|
||||
valueKind: row => Column.ValueKind.Present,
|
||||
valueKind: row => Column.ValueKinds.Present,
|
||||
toArray: params => ColumnHelpers.createAndFillArray(rowCount, value, params),
|
||||
areValuesEqual: areValuesEqualProvider(tokens)
|
||||
};
|
||||
|
||||
@@ -87,13 +87,21 @@ export function parseFloat(str: string, start: number, end: number) {
|
||||
return neg * ret;
|
||||
}
|
||||
|
||||
export const enum NumberType {
|
||||
Int,
|
||||
Float,
|
||||
Scientific,
|
||||
NaN
|
||||
export const enum NumberTypes {
|
||||
Int = 0,
|
||||
Float = 1,
|
||||
Scientific = 2,
|
||||
NaN = 3
|
||||
}
|
||||
|
||||
export const NumberType = {
|
||||
Int: NumberTypes.Int,
|
||||
Float: NumberTypes.Float,
|
||||
Scientific: NumberTypes.Scientific,
|
||||
NaN: NumberTypes.NaN
|
||||
} as const;
|
||||
export type NumberType = (typeof NumberType)[keyof typeof NumberType];
|
||||
|
||||
function isInt(str: string, start: number, end: number) {
|
||||
if (str.charCodeAt(start) === 45 /* - */) { start++; }
|
||||
for (; start < end; start++) {
|
||||
@@ -107,7 +115,7 @@ function isInt(str: string, start: number, end: number) {
|
||||
function getNumberTypeScientific(str: string, start: number, end: number) {
|
||||
// handle + in '1e+1' separately.
|
||||
if (str.charCodeAt(start) === 43 /* + */) start++;
|
||||
return isInt(str, start, end) ? NumberType.Scientific : NumberType.NaN;
|
||||
return isInt(str, start, end) ? NumberTypes.Scientific : NumberTypes.NaN;
|
||||
}
|
||||
|
||||
/** The whole range must match, otherwise returns NaN */
|
||||
@@ -121,7 +129,7 @@ export function getNumberType(str: string): NumberType {
|
||||
|
||||
// string is . or -.
|
||||
if (str.charCodeAt(start) === 46 && end - start === 1) {
|
||||
return NumberType.NaN;
|
||||
return NumberTypes.NaN;
|
||||
}
|
||||
|
||||
while (start < end) {
|
||||
@@ -139,18 +147,18 @@ export function getNumberType(str: string): NumberType {
|
||||
} else if (c === 53 || c === 21) { // 'e'/'E'
|
||||
return getNumberTypeScientific(str, start + 1, end);
|
||||
} else {
|
||||
return NumberType.NaN;
|
||||
return NumberTypes.NaN;
|
||||
}
|
||||
}
|
||||
return hasDigit ? NumberType.Float : NumberType.Int;
|
||||
return hasDigit ? NumberTypes.Float : NumberTypes.Int;
|
||||
} else if (c === 53 || c === 21) { // 'e'/'E'
|
||||
if (start === 0 || start === 1 && str.charCodeAt(0) === 45) {
|
||||
return NumberType.NaN; // string starts with e/E or -e/-E
|
||||
return NumberTypes.NaN; // string starts with e/E or -e/-E
|
||||
}
|
||||
return getNumberTypeScientific(str, start + 1, end);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start === end ? NumberType.Int : NumberType.NaN;
|
||||
return start === end ? NumberTypes.Int : NumberTypes.NaN;
|
||||
}
|
||||
|
||||
@@ -184,8 +184,8 @@ function getFieldData(field: Field<any, any>, arrayCtor: ArrayCtor<string | numb
|
||||
const keys = data[_d].keys();
|
||||
while (keys.hasNext) {
|
||||
const key = keys.move();
|
||||
const p = valueKind ? valueKind(key, d) : Column.ValueKind.Present;
|
||||
if (p !== Column.ValueKind.Present) {
|
||||
const p = valueKind ? valueKind(key, d) : Column.ValueKinds.Present;
|
||||
if (p !== Column.ValueKinds.Present) {
|
||||
mask[offset] = p;
|
||||
if (isStr)
|
||||
array[offset] = '';
|
||||
@@ -193,10 +193,10 @@ function getFieldData(field: Field<any, any>, arrayCtor: ArrayCtor<string | numb
|
||||
} else {
|
||||
const value = getter(key, d, offset);
|
||||
if (typeof value === 'string' && !value) {
|
||||
mask[offset] = Column.ValueKind.NotPresent;
|
||||
mask[offset] = Column.ValueKinds.NotPresent;
|
||||
allPresent = false;
|
||||
} else {
|
||||
mask[offset] = Column.ValueKind.Present;
|
||||
mask[offset] = Column.ValueKinds.Present;
|
||||
}
|
||||
array[offset] = value;
|
||||
}
|
||||
|
||||
@@ -82,9 +82,9 @@ export class TextEncoder implements Encoder<string> {
|
||||
|
||||
function writeValue(builder: StringBuilder, data: any, key: any, f: Field<any, any>, floatPrecision: number, index: number): boolean {
|
||||
const kind = f.valueKind;
|
||||
const p = kind ? kind(key, data) : Column.ValueKind.Present;
|
||||
if (p !== Column.ValueKind.Present) {
|
||||
if (p === Column.ValueKind.NotPresent) writeNotPresent(builder);
|
||||
const p = kind ? kind(key, data) : Column.ValueKinds.Present;
|
||||
if (p !== Column.ValueKinds.Present) {
|
||||
if (p === Column.ValueKinds.NotPresent) writeNotPresent(builder);
|
||||
else writeUnknown(builder);
|
||||
} else {
|
||||
const val = f.value(key, data, index);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -9,10 +9,17 @@ import { CentroidHelper } from './centroid-helper';
|
||||
import { Sphere3D } from '../geometry';
|
||||
import { Box3D } from './primitives/box3d';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3dot = Vec3.dot;
|
||||
const v3copy = Vec3.copy;
|
||||
const v3scaleAndSub = Vec3.scaleAndSub;
|
||||
const v3scaleAndAdd = Vec3.scaleAndAdd;
|
||||
|
||||
// implementing http://www.ep.liu.se/ecp/034/009/ecp083409.pdf
|
||||
|
||||
export class BoundaryHelper {
|
||||
private dir: Vec3[];
|
||||
private dirLength: number;
|
||||
|
||||
private minDist: number[] = [];
|
||||
private maxDist: number[] = [];
|
||||
@@ -20,28 +27,29 @@ export class BoundaryHelper {
|
||||
centroidHelper = new CentroidHelper();
|
||||
|
||||
private computeExtrema(i: number, p: Vec3) {
|
||||
const d = Vec3.dot(this.dir[i], p);
|
||||
const d = v3dot(this.dir[i], p);
|
||||
|
||||
if (d < this.minDist[i]) {
|
||||
this.minDist[i] = d;
|
||||
Vec3.copy(this.extrema[i * 2], p);
|
||||
v3copy(this.extrema[i * 2], p);
|
||||
}
|
||||
if (d > this.maxDist[i]) {
|
||||
this.maxDist[i] = d;
|
||||
Vec3.copy(this.extrema[i * 2 + 1], p);
|
||||
v3copy(this.extrema[i * 2 + 1], p);
|
||||
}
|
||||
}
|
||||
|
||||
private computeSphereExtrema(i: number, center: Vec3, radius: number) {
|
||||
const d = Vec3.dot(this.dir[i], center);
|
||||
const di = this.dir[i];
|
||||
const d = v3dot(di, center);
|
||||
|
||||
if (d - radius < this.minDist[i]) {
|
||||
this.minDist[i] = d - radius;
|
||||
Vec3.scaleAndSub(this.extrema[i * 2], center, this.dir[i], radius);
|
||||
v3scaleAndSub(this.extrema[i * 2], center, di, radius);
|
||||
}
|
||||
if (d + radius > this.maxDist[i]) {
|
||||
this.maxDist[i] = d + radius;
|
||||
Vec3.scaleAndAdd(this.extrema[i * 2 + 1], center, this.dir[i], radius);
|
||||
v3scaleAndAdd(this.extrema[i * 2 + 1], center, di, radius);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +64,13 @@ export class BoundaryHelper {
|
||||
}
|
||||
|
||||
includePosition(p: Vec3) {
|
||||
for (let i = 0, il = this.dir.length; i < il; ++i) {
|
||||
for (let i = 0; i < this.dirLength; ++i) {
|
||||
this.computeExtrema(i, p);
|
||||
}
|
||||
}
|
||||
|
||||
includePositionRadius(center: Vec3, radius: number) {
|
||||
for (let i = 0, il = this.dir.length; i < il; ++i) {
|
||||
for (let i = 0; i < this.dirLength; ++i) {
|
||||
this.computeSphereExtrema(i, center, radius);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +101,7 @@ export class BoundaryHelper {
|
||||
}
|
||||
|
||||
getSphere(sphere?: Sphere3D) {
|
||||
return Sphere3D.setExtrema(this.centroidHelper.getSphere(sphere), [...this.extrema]);
|
||||
return Sphere3D.setExtrema(this.centroidHelper.getSphere(sphere), this.extrema.slice());
|
||||
}
|
||||
|
||||
getBox(box?: Box3D) {
|
||||
@@ -101,7 +109,7 @@ export class BoundaryHelper {
|
||||
}
|
||||
|
||||
reset() {
|
||||
for (let i = 0, il = this.dir.length; i < il; ++i) {
|
||||
for (let i = 0; i < this.dirLength; ++i) {
|
||||
this.minDist[i] = Infinity;
|
||||
this.maxDist[i] = -Infinity;
|
||||
this.extrema[i * 2] = Vec3();
|
||||
@@ -112,6 +120,7 @@ export class BoundaryHelper {
|
||||
|
||||
constructor(quality: EposQuality) {
|
||||
this.dir = getEposDir(quality);
|
||||
this.dirLength = this.dir.length;
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user