mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 15:14:22 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5ecf5648e | ||
|
|
821f82fc3f | ||
|
|
92305fe628 | ||
|
|
17fe57b8a5 | ||
|
|
47433a51d3 | ||
|
|
e090827ced | ||
|
|
98afc27442 | ||
|
|
9d4f28a395 | ||
|
|
50266d9a56 | ||
|
|
b23d610c94 | ||
|
|
119c43d527 | ||
|
|
124feeb790 | ||
|
|
2c0e7e84da | ||
|
|
0d1e105343 | ||
|
|
f040c89ab3 | ||
|
|
5e9d8298ef | ||
|
|
7766ca2793 | ||
|
|
fb2f22f120 | ||
|
|
146fed3504 | ||
|
|
0b7a6e3375 | ||
|
|
f1fbdeaca0 | ||
|
|
ee7e37f6bc | ||
|
|
861f665ab3 | ||
|
|
456de23ad4 | ||
|
|
6d3578c17e | ||
|
|
57da7267e2 | ||
|
|
578b764406 | ||
|
|
f65a38a085 | ||
|
|
b87beb4a6e | ||
|
|
62a58facb2 | ||
|
|
5fa8178df7 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -6,6 +6,23 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v3.7.0] - 2022-04-13
|
||||
|
||||
- Fix ``xrayShaded`` for texture-mesh geometries
|
||||
- [Breaking] Change ``allowTransparentBackfaces`` to ``transparentBackfaces`` with options ``off``, ``on``, ``opaque``. This was only added in 3.6.0, so allowing a breaking change here.
|
||||
- ``off``: don't show (default)
|
||||
- ``on``: show with transparency
|
||||
- ``opaque``: show fully opaque
|
||||
- Add option to disable file drop overlay.
|
||||
|
||||
## [v3.6.2] - 2022-04-05
|
||||
|
||||
- ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
|
||||
- React 18 friendly ``useBehavior`` hook.
|
||||
|
||||
## [v3.6.1] - 2022-04-03
|
||||
|
||||
- Fix React18 related UI regressions.
|
||||
|
||||
## [v3.6.0] - 2022-04-03
|
||||
|
||||
@@ -19,8 +36,8 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
- Fix handling of case insensitive mmCIF enumeration fields (including entity.type)
|
||||
- Fix ``disable-wboit`` Viewer GET param
|
||||
- Add support for React 18.
|
||||
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
|
||||
- In Mol* 4.0, React 18 will become the default option.
|
||||
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
|
||||
- In Mol* 4.0, React 18 will become the default option.
|
||||
|
||||
## [v3.5.0] - 2022-03-25
|
||||
|
||||
|
||||
2531
package-lock.json
generated
2531
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "3.6.0",
|
||||
"version": "3.7.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -94,23 +94,23 @@
|
||||
"@graphql-codegen/add": "^3.1.1",
|
||||
"@graphql-codegen/cli": "^2.6.2",
|
||||
"@graphql-codegen/time": "^3.1.1",
|
||||
"@graphql-codegen/typescript": "^2.4.7",
|
||||
"@graphql-codegen/typescript": "^2.4.8",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.4.2",
|
||||
"@graphql-codegen/typescript-operations": "^2.3.4",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.4.5",
|
||||
"@graphql-codegen/typescript-operations": "^2.3.5",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/react": "^17.0.43",
|
||||
"@types/react-dom": "^17.0.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||
"@typescript-eslint/parser": "^5.14.0",
|
||||
"@types/react": "^18.0.1",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.18.0",
|
||||
"@typescript-eslint/parser": "^5.18.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^7.0.0",
|
||||
"concurrently": "^7.1.0",
|
||||
"cpx2": "^4.2.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint": "^8.13.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
@@ -122,14 +122,14 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"sass": "^1.49.9",
|
||||
"sass": "^1.50.0",
|
||||
"sass-loader": "^12.6.0",
|
||||
"simple-git": "^3.3.0",
|
||||
"simple-git": "^3.5.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.1.3",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.6.3",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -141,7 +141,7 @@
|
||||
"@types/node-fetch": "^2.6.1",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
"body-parser": "^1.19.2",
|
||||
"body-parser": "^1.20.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.3",
|
||||
@@ -150,7 +150,7 @@
|
||||
"immutable": "^4.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"rxjs": "^7.5.5",
|
||||
"swagger-ui-dist": "^4.6.2",
|
||||
"swagger-ui-dist": "^4.10.3",
|
||||
"tslib": "^2.3.1",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
|
||||
@@ -80,20 +80,24 @@ export class AlphaOrbitalsExample {
|
||||
|
||||
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Error',
|
||||
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
|
||||
this.plugin.behaviors.canvas3d.initialized.subscribe(init => {
|
||||
if (!init) return;
|
||||
|
||||
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Error',
|
||||
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
});
|
||||
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
}
|
||||
|
||||
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -11,7 +11,7 @@ import { Color } from '../../mol-util/color';
|
||||
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { LocationIterator } from '../util/location-iterator';
|
||||
import { NullLocation } from '../../mol-model/location';
|
||||
import { LocationColor, ColorTheme } from '../../mol-theme/color';
|
||||
import { LocationColor, ColorTheme, ColorVolume } from '../../mol-theme/color';
|
||||
import { Geometry } from './geometry';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
@@ -48,8 +48,18 @@ function _createColors(locationIt: LocationIterator, positionIt: LocationIterato
|
||||
case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData);
|
||||
case 'vertex': return createVertexColor(positionIt, colorTheme.color, colorData);
|
||||
case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
|
||||
case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
|
||||
case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
|
||||
case 'volume':
|
||||
if (colorTheme.grid) {
|
||||
return createGridColor(colorTheme.grid, 'volume', colorData);
|
||||
} else {
|
||||
throw new Error('Grid missing for "volume" color theme');
|
||||
}
|
||||
case 'volumeInstance':
|
||||
if (colorTheme.grid) {
|
||||
return createGridColor(colorTheme.grid, 'volumeInstance', colorData);
|
||||
} else {
|
||||
throw new Error('Grid missing for "volume" color theme');
|
||||
}
|
||||
case 'direct': return createDirectColor(colorData);
|
||||
}
|
||||
}
|
||||
@@ -207,12 +217,6 @@ function createVertexInstanceColor(locationIt: LocationIterator, color: Location
|
||||
|
||||
//
|
||||
|
||||
interface ColorVolume {
|
||||
colors: Texture
|
||||
dimension: Vec3
|
||||
transform: Vec4
|
||||
}
|
||||
|
||||
export function createGridColor(grid: ColorVolume, type: ColorType, colorData?: ColorData): ColorData {
|
||||
const { colors, dimension, transform } = grid;
|
||||
const width = colors.getWidth();
|
||||
|
||||
@@ -157,7 +157,7 @@ export namespace Cylinders {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
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),
|
||||
};
|
||||
@@ -242,6 +242,7 @@ export namespace Cylinders {
|
||||
uDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
@@ -259,6 +260,7 @@ export namespace Cylinders {
|
||||
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque');
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -625,7 +625,7 @@ export namespace Mesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
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),
|
||||
};
|
||||
@@ -701,6 +701,7 @@ export namespace Mesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
|
||||
@@ -721,6 +722,7 @@ export namespace Mesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque');
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export namespace Spheres {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
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),
|
||||
};
|
||||
@@ -209,6 +209,7 @@ export namespace Spheres {
|
||||
uDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
@@ -226,6 +227,7 @@ export namespace Spheres {
|
||||
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque');
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
|
||||
export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
@@ -113,7 +114,7 @@ export namespace TextureMesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
allowTransparentBackfaces: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
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),
|
||||
};
|
||||
@@ -126,8 +127,8 @@ export namespace TextureMesh {
|
||||
createValuesSimple,
|
||||
updateValues,
|
||||
updateBoundingSphere,
|
||||
createRenderableState: BaseGeometry.createRenderableState,
|
||||
updateRenderableState: BaseGeometry.updateRenderableState,
|
||||
createRenderableState,
|
||||
updateRenderableState,
|
||||
createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation)
|
||||
};
|
||||
|
||||
@@ -173,6 +174,7 @@ export namespace TextureMesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
|
||||
@@ -193,6 +195,7 @@ export namespace TextureMesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque');
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
@@ -209,4 +212,16 @@ export namespace TextureMesh {
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
|
||||
function createRenderableState(props: PD.Values<Params>): RenderableState {
|
||||
const state = BaseGeometry.createRenderableState(props);
|
||||
updateRenderableState(state, props);
|
||||
return state;
|
||||
}
|
||||
|
||||
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
BaseGeometry.updateRenderableState(state, props);
|
||||
state.opaque = state.opaque && !props.xrayShaded;
|
||||
state.writeDepth = state.opaque;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -26,6 +26,7 @@ export const CylindersSchema = {
|
||||
uDoubleSided: UniformSpec('b'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dOpaqueBackfaces: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -22,6 +22,7 @@ export const MeshSchema = {
|
||||
dFlipSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dOpaqueBackfaces: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
meta: ValueSpec('unknown')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -23,6 +23,7 @@ export const SpheresSchema = {
|
||||
uDoubleSided: UniformSpec('b'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dOpaqueBackfaces: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -23,6 +23,7 @@ export const TextureMeshSchema = {
|
||||
dFlipSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
dOpaqueBackfaces: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
meta: ValueSpec('unknown')
|
||||
|
||||
@@ -135,6 +135,12 @@ function getLight(props: RendererProps['light'], light?: Light): Light {
|
||||
}
|
||||
|
||||
namespace Renderer {
|
||||
const enum Flag {
|
||||
None = 0,
|
||||
BlendedFront = 1,
|
||||
BlendedBack = 2
|
||||
}
|
||||
|
||||
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
||||
const { gl, state, stats } = ctx;
|
||||
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
|
||||
@@ -220,7 +226,7 @@ namespace Renderer {
|
||||
|
||||
let globalUniformsNeedUpdate = true;
|
||||
|
||||
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
|
||||
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant, flag: Flag) => {
|
||||
if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant === 'pick')) {
|
||||
return;
|
||||
}
|
||||
@@ -259,6 +265,24 @@ namespace Renderer {
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.depthMask(false);
|
||||
}
|
||||
} else if (flag === Flag.BlendedFront) {
|
||||
state.enable(gl.CULL_FACE);
|
||||
if (r.values.dFlipSided?.ref.value) {
|
||||
state.frontFace(gl.CW);
|
||||
state.cullFace(gl.FRONT);
|
||||
} else {
|
||||
state.frontFace(gl.CCW);
|
||||
state.cullFace(gl.BACK);
|
||||
}
|
||||
} else if (flag === Flag.BlendedBack) {
|
||||
state.enable(gl.CULL_FACE);
|
||||
if (r.values.dFlipSided?.ref.value) {
|
||||
state.frontFace(gl.CW);
|
||||
state.cullFace(gl.BACK);
|
||||
} else {
|
||||
state.frontFace(gl.CCW);
|
||||
state.cullFace(gl.FRONT);
|
||||
}
|
||||
} else {
|
||||
if (r.values.uDoubleSided) {
|
||||
if (r.values.uDoubleSided.ref.value || r.values.hasReflection.ref.value) {
|
||||
@@ -271,14 +295,9 @@ namespace Renderer {
|
||||
state.disable(gl.CULL_FACE);
|
||||
}
|
||||
|
||||
if (r.values.dFlipSided) {
|
||||
if (r.values.dFlipSided.ref.value) {
|
||||
state.frontFace(gl.CW);
|
||||
state.cullFace(gl.FRONT);
|
||||
} else {
|
||||
state.frontFace(gl.CCW);
|
||||
state.cullFace(gl.BACK);
|
||||
}
|
||||
if (r.values.dFlipSided?.ref.value) {
|
||||
state.frontFace(gl.CW);
|
||||
state.cullFace(gl.FRONT);
|
||||
} else {
|
||||
// webgl default
|
||||
state.frontFace(gl.CCW);
|
||||
@@ -342,7 +361,7 @@ namespace Renderer {
|
||||
const { renderables } = group;
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
if (!renderables[i].state.colorOnly) {
|
||||
renderObject(renderables[i], variant);
|
||||
renderObject(renderables[i], variant, Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -356,7 +375,7 @@ namespace Renderer {
|
||||
|
||||
const { renderables } = group;
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
renderObject(renderables[i], 'depth');
|
||||
renderObject(renderables[i], 'depth', Flag.None);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -373,7 +392,7 @@ namespace Renderer {
|
||||
const r = renderables[i];
|
||||
|
||||
if (r.values.markerAverage.ref.value !== 1) {
|
||||
renderObject(renderables[i], 'marking');
|
||||
renderObject(renderables[i], 'marking', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -391,7 +410,7 @@ namespace Renderer {
|
||||
const r = renderables[i];
|
||||
|
||||
if (r.values.markerAverage.ref.value > 0) {
|
||||
renderObject(renderables[i], 'marking');
|
||||
renderObject(renderables[i], 'marking', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -412,7 +431,9 @@ namespace Renderer {
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (r.state.opaque) {
|
||||
renderObject(r, 'colorBlended');
|
||||
renderObject(r, 'colorBlended', Flag.None);
|
||||
} else if (r.values.uDoubleSided?.ref.value && r.values.dOpaqueBackfaces?.ref.value) {
|
||||
renderObject(r, 'colorBlended', Flag.BlendedBack);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -435,7 +456,7 @@ namespace Renderer {
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (!r.state.opaque && r.state.writeDepth) {
|
||||
renderObject(r, 'colorBlended');
|
||||
renderObject(r, 'colorBlended', Flag.None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +464,15 @@ namespace Renderer {
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (!r.state.opaque && !r.state.writeDepth) {
|
||||
renderObject(r, 'colorBlended');
|
||||
if (r.values.uDoubleSided?.ref.value) {
|
||||
// render frontfaces and backfaces separately to avoid artefacts
|
||||
if (!r.values.dOpaqueBackfaces?.ref.value) {
|
||||
renderObject(r, 'colorBlended', Flag.BlendedBack);
|
||||
}
|
||||
renderObject(r, 'colorBlended', Flag.BlendedFront);
|
||||
} else {
|
||||
renderObject(r, 'colorBlended', Flag.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -462,7 +491,7 @@ namespace Renderer {
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
|
||||
renderObject(r, 'colorBlended');
|
||||
renderObject(r, 'colorBlended', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -481,7 +510,7 @@ namespace Renderer {
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
|
||||
renderObject(r, 'colorBlended');
|
||||
renderObject(r, 'colorBlended', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -500,8 +529,8 @@ namespace Renderer {
|
||||
// TODO: simplify, handle in renderable.state???
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
|
||||
renderObject(r, 'colorWboit');
|
||||
if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dOpaqueBackfaces?.ref.value) {
|
||||
renderObject(r, 'colorWboit', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -517,7 +546,7 @@ namespace Renderer {
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
|
||||
renderObject(r, 'colorWboit');
|
||||
renderObject(r, 'colorWboit', Flag.None);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,5 +5,9 @@ if (interior) {
|
||||
} else {
|
||||
gl_FragColor.rgb *= 1.0 - uInteriorDarkening;
|
||||
}
|
||||
|
||||
#ifdef dOpaqueBackfaces
|
||||
gl_FragColor.a = 1.0;
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
@@ -2,6 +2,12 @@ export const check_picking_alpha = `
|
||||
float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
|
||||
float alpha = (1.0 - fogFactor) * uAlpha;
|
||||
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1)
|
||||
discard; // ignore so the element below can be picked
|
||||
// if not opaque enough ignore so the element below can be picked
|
||||
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1) {
|
||||
#ifdef dOpaqueBackfaces
|
||||
if (!interior) discard;
|
||||
#else
|
||||
discard;
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
@@ -19,6 +19,8 @@ uniform float uBumpiness;
|
||||
uniform vec3 uColorGridDim;
|
||||
uniform vec4 uColorGridTransform;
|
||||
uniform sampler2D tColorGrid;
|
||||
#elif defined(dColorType_direct)
|
||||
varying vec4 vColor;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
|
||||
@@ -9,12 +9,14 @@ import { Writer } from './writer';
|
||||
import { Encoder, Category, Field } from './cif/encoder';
|
||||
import { ComponentAtom } from '../../mol-model-formats/structure/property/atoms/chem_comp';
|
||||
import { ComponentBond } from '../../mol-model-formats/structure/property/bonds/chem_comp';
|
||||
import { getElementIdx, isHydrogen } from '../../mol-model/structure/structure/unit/bonds/common';
|
||||
import { ElementSymbol } from '../../mol-model/structure/model/types';
|
||||
|
||||
interface Atom {
|
||||
Cartn_x: number,
|
||||
Cartn_y: number,
|
||||
Cartn_z: number,
|
||||
type_symbol: string,
|
||||
type_symbol: ElementSymbol,
|
||||
index: number
|
||||
}
|
||||
|
||||
@@ -109,11 +111,12 @@ export abstract class LigandEncoder implements Encoder<string> {
|
||||
const key = it.move();
|
||||
|
||||
const lai = label_atom_id.value(key, data, index) as string;
|
||||
const ts = type_symbol.value(key, data, index) as string;
|
||||
if (this.skipHydrogen(ts)) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
// ignore all alternate locations after the first
|
||||
if (atoms.has(lai)) continue;
|
||||
|
||||
const ts = type_symbol.value(key, data, index) as ElementSymbol;
|
||||
if (this.skipHydrogen(ts)) continue;
|
||||
|
||||
const a: { [k: string]: (string | number) } = {};
|
||||
|
||||
for (let _f = 0, _fl = fields.length; _f < _fl; _f++) {
|
||||
@@ -131,11 +134,15 @@ export abstract class LigandEncoder implements Encoder<string> {
|
||||
return atoms;
|
||||
}
|
||||
|
||||
protected skipHydrogen(type_symbol: string) {
|
||||
protected skipHydrogen(type_symbol: ElementSymbol) {
|
||||
if (this.hydrogens) {
|
||||
return false;
|
||||
}
|
||||
return type_symbol === 'H';
|
||||
return this.isHydrogen(type_symbol);
|
||||
}
|
||||
|
||||
protected isHydrogen(type_symbol: ElementSymbol) {
|
||||
return isHydrogen(getElementIdx(type_symbol));
|
||||
}
|
||||
|
||||
private getSortedFields<Ctx>(instance: Category.Instance<Ctx>, names: string[]) {
|
||||
|
||||
@@ -26,14 +26,27 @@ export class MolEncoder extends LigandEncoder {
|
||||
|
||||
const atomMap = this.componentAtomData.entries.get(name)!;
|
||||
const bondMap = this.componentBondData.entries.get(name)!;
|
||||
// happens for the unknown ligands (UNL)
|
||||
if (!atomMap) throw Error(`The Chemical Component Dictionary doesn't hold any atom data for ${name}`);
|
||||
|
||||
let bondCount = 0;
|
||||
let chiral = false;
|
||||
|
||||
// traverse once to determine all actually present atoms
|
||||
const atoms = this.getAtoms(instance, source);
|
||||
atoms.forEach((atom1, label_atom_id1) => {
|
||||
const { index: i1 } = atom1;
|
||||
const { charge, stereo_config } = atomMap.map.get(label_atom_id1)!;
|
||||
const { index: i1, type_symbol: type_symbol1 } = atom1;
|
||||
const atomMapData1 = atomMap.map.get(label_atom_id1);
|
||||
|
||||
if (!atomMapData1) {
|
||||
if (this.isHydrogen(type_symbol1)) {
|
||||
return;
|
||||
} else {
|
||||
throw Error(`Unknown atom ${label_atom_id1} for component ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const { charge, stereo_config } = atomMapData1;
|
||||
StringBuilder.writePadLeft(ctab, atom1.Cartn_x.toFixed(4), 10);
|
||||
StringBuilder.writePadLeft(ctab, atom1.Cartn_y.toFixed(4), 10);
|
||||
StringBuilder.writePadLeft(ctab, atom1.Cartn_z.toFixed(4), 10);
|
||||
@@ -50,8 +63,8 @@ export class MolEncoder extends LigandEncoder {
|
||||
const atom2 = atoms.get(label_atom_id2);
|
||||
if (!atom2) return;
|
||||
|
||||
const { index: i2, type_symbol: type_symbol2 } = atom2;
|
||||
if (i1 < i2 && !this.skipHydrogen(type_symbol2)) {
|
||||
const { index: i2 } = atom2;
|
||||
if (i1 < i2) {
|
||||
const { order } = bond;
|
||||
StringBuilder.writeIntegerPadLeft(bonds, i1 + 1, 3);
|
||||
StringBuilder.writeIntegerPadLeft(bonds, i2 + 1, 3);
|
||||
|
||||
@@ -29,21 +29,34 @@ export class Mol2Encoder extends LigandEncoder {
|
||||
const name = this.getName(instance, source);
|
||||
StringBuilder.writeSafe(this.builder, `# Name: ${name}\n# Created by ${this.encoder}\n\n`);
|
||||
|
||||
const atomMap = this.componentAtomData.entries.get(name)!;
|
||||
const bondMap = this.componentBondData.entries.get(name)!;
|
||||
// happens for the unknown ligands (UNL)
|
||||
if (!atomMap) throw Error(`The Chemical Component Dictionary doesn't hold any atom data for ${name}`);
|
||||
let bondCount = 0;
|
||||
|
||||
const atoms = this.getAtoms(instance, source);
|
||||
StringBuilder.writeSafe(a, '@<TRIPOS>ATOM\n');
|
||||
StringBuilder.writeSafe(b, '@<TRIPOS>BOND\n');
|
||||
atoms.forEach((atom1, label_atom_id1) => {
|
||||
const { index: i1 } = atom1;
|
||||
const { index: i1, type_symbol: type_symbol1 } = atom1;
|
||||
const atomMapData1 = atomMap.map.get(label_atom_id1);
|
||||
|
||||
if (!atomMapData1) {
|
||||
if (this.isHydrogen(type_symbol1)) {
|
||||
return;
|
||||
} else {
|
||||
throw Error(`Unknown atom ${label_atom_id1} for component ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (bondMap?.map) {
|
||||
bondMap.map.get(label_atom_id1)!.forEach((bond, label_atom_id2) => {
|
||||
const atom2 = atoms.get(label_atom_id2);
|
||||
if (!atom2) return;
|
||||
|
||||
const { index: i2, type_symbol: type_symbol2 } = atom2;
|
||||
if (i1 < i2 && !this.skipHydrogen(type_symbol2)) {
|
||||
const { index: i2 } = atom2;
|
||||
if (i1 < i2) {
|
||||
const { order, flags } = bond;
|
||||
const ar = BondType.is(BondType.Flag.Aromatic, flags);
|
||||
StringBuilder.writeSafe(b, `${++bondCount} ${i1 + 1} ${i2 + 1} ${ar ? 'ar' : order}`);
|
||||
@@ -52,7 +65,7 @@ export class Mol2Encoder extends LigandEncoder {
|
||||
});
|
||||
}
|
||||
|
||||
const sybyl = bondMap?.map ? this.mapToSybyl(label_atom_id1, atom1.type_symbol, bondMap) : atom1.type_symbol;
|
||||
const sybyl = bondMap?.map ? this.mapToSybyl(label_atom_id1, type_symbol1, bondMap) : type_symbol1;
|
||||
StringBuilder.writeSafe(a, `${i1 + 1} ${label_atom_id1} ${atom1.Cartn_x.toFixed(3)} ${atom1.Cartn_y.toFixed(3)} ${atom1.Cartn_z.toFixed(3)} ${sybyl} 1 ${name} 0.000\n`);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Icon, ArrowRightSvg, ArrowDropDownSvg } from './controls/icons';
|
||||
|
||||
export const PluginReactContext = React.createContext(void 0 as any as PluginUIContext);
|
||||
|
||||
export abstract class PluginUIComponent<P = {}, S = {}, SS = {}> extends React.Component<P, S, SS> {
|
||||
export abstract class PluginUIComponent<P = {}, S = {}, SS = {}> extends React.Component<P & { children?: any }, S, SS> {
|
||||
static contextType = PluginReactContext;
|
||||
readonly plugin: PluginUIContext;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
|
||||
}
|
||||
}
|
||||
|
||||
let _colors: React.ReactFragment | undefined = void 0;
|
||||
let _colors: any = void 0;
|
||||
export function ColorOptions() {
|
||||
if (_colors) return _colors;
|
||||
_colors = <>{DefaultColorSwatch.map(v =>
|
||||
|
||||
@@ -21,7 +21,8 @@ export class ControlGroup extends React.Component<{
|
||||
onHeaderClick?: () => void,
|
||||
noTopMargin?: boolean,
|
||||
childrenClassName?: string,
|
||||
maxHeight?: string
|
||||
maxHeight?: string,
|
||||
children?: any
|
||||
}, { isExpanded: boolean }> {
|
||||
state = { isExpanded: !!this.props.initialExpanded };
|
||||
|
||||
@@ -335,7 +336,7 @@ export class ToggleButton extends React.PureComponent<ToggleButtonProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpandGroup extends React.PureComponent<{ header: string, headerStyle?: React.CSSProperties, initiallyExpanded?: boolean, accent?: boolean, noOffset?: boolean, marginTop?: 0 | string, headerLeftMargin?: string }, { isExpanded: boolean }> {
|
||||
export class ExpandGroup extends React.PureComponent<{ children?: any, header: string, headerStyle?: React.CSSProperties, initiallyExpanded?: boolean, accent?: boolean, noOffset?: boolean, marginTop?: 0 | string, headerLeftMargin?: string }, { isExpanded: boolean }> {
|
||||
state = { isExpanded: !!this.props.initiallyExpanded };
|
||||
|
||||
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2020-21 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-22 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { skip } from 'rxjs';
|
||||
|
||||
interface Behavior<T> {
|
||||
value: T;
|
||||
subscribe(f: (v: T) => void): { unsubscribe(): void };
|
||||
}
|
||||
|
||||
export function useBehavior<T>(s: Behavior<T>): T;
|
||||
// eslint-disable-next-line
|
||||
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined;
|
||||
// eslint-disable-next-line
|
||||
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
|
||||
const [, next] = useState({});
|
||||
const current = useRef<T>();
|
||||
function useBehaviorLegacy<T>(s: Behavior<T> | undefined): T | undefined {
|
||||
const [, next] = React.useState({});
|
||||
const current = React.useRef<T>();
|
||||
current.current = s?.value;
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@@ -32,4 +29,29 @@ export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
|
||||
}, [s]);
|
||||
|
||||
return s?.value;
|
||||
}
|
||||
|
||||
function useBehaviorReact18<T>(s: Behavior<T> | undefined) {
|
||||
return (React as any).useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(callback: () => void) => {
|
||||
const sub = (s as any)?.pipe!(skip(1)).subscribe(callback)!;
|
||||
return () => sub?.unsubscribe();
|
||||
},
|
||||
[s]
|
||||
),
|
||||
React.useCallback(() => s?.value, [s])
|
||||
);
|
||||
}
|
||||
|
||||
const _useBehavior = !!(React as any).useSyncExternalStore
|
||||
? useBehaviorReact18
|
||||
: useBehaviorLegacy;
|
||||
|
||||
export function useBehavior<T>(s: Behavior<T>): T;
|
||||
// eslint-disable-next-line
|
||||
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined;
|
||||
// eslint-disable-next-line
|
||||
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
|
||||
return _useBehavior(s);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import { Asset } from '../mol-util/assets';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { useBehavior } from './hooks/use-behavior';
|
||||
|
||||
export class Plugin extends React.Component<{ plugin: PluginUIContext }, {}> {
|
||||
export class Plugin extends React.Component<{ plugin: PluginUIContext, children?: any }, {}> {
|
||||
region(kind: 'left' | 'right' | 'bottom' | 'main', element: JSX.Element) {
|
||||
return <div className={`msp-layout-region msp-layout-${kind}`}>
|
||||
<div className='msp-layout-static'>
|
||||
@@ -39,7 +39,7 @@ export class Plugin extends React.Component<{ plugin: PluginUIContext }, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
export class PluginContextContainer extends React.Component<{ plugin: PluginUIContext }> {
|
||||
export class PluginContextContainer extends React.Component<{ plugin: PluginUIContext, children?: any }> {
|
||||
render() {
|
||||
return <PluginReactContext.Provider value={this.props.plugin}>
|
||||
<div className='msp-plugin'>
|
||||
@@ -159,7 +159,7 @@ class Layout extends PluginUIComponent {
|
||||
{layout.showControls && controls.bottom !== 'none' && this.region('bottom', controls.bottom || Log)}
|
||||
</div>
|
||||
{!this.plugin.spec.components?.hideTaskOverlay && <OverlayTaskProgress />}
|
||||
<DragOverlay plugin={this.plugin} showDragOverlay={this.showDragOverlay} />
|
||||
{!this.plugin.spec.components?.disableDragOverlay && <DragOverlay plugin={this.plugin} showDragOverlay={this.showDragOverlay} />}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ interface PluginUISpec extends PluginSpec {
|
||||
view?: React.ComponentClass,
|
||||
controls?: React.ComponentClass
|
||||
},
|
||||
hideTaskOverlay?: boolean
|
||||
hideTaskOverlay?: boolean,
|
||||
disableDragOverlay?: boolean,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PurePluginUIComponent } from '../base';
|
||||
import { ParameterControls, ParamOnChange } from '../controls/parameters';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BehaviorSubject, skip } from 'rxjs';
|
||||
import { Icon, RefreshSvg, CheckSvg, ArrowRightSvg, ArrowDropDownSvg, TuneSvg } from '../controls/icons';
|
||||
import { ExpandGroup, ToggleButton, Button, IconButton } from '../controls/common';
|
||||
|
||||
@@ -124,7 +124,7 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
|
||||
abstract getSourceAndTarget(): { a?: StateObject, b?: StateObject, bCell?: StateObjectCell };
|
||||
abstract state: S;
|
||||
|
||||
private busy: Subject<boolean> = new Subject();
|
||||
private busy = new BehaviorSubject(false);
|
||||
|
||||
private onEnter = () => {
|
||||
if (this.state.error) return;
|
||||
@@ -167,11 +167,11 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.subscribe(this.plugin.behaviors.state.isBusy, b => {
|
||||
if (this.state.busy !== b) this.busy.next(b);
|
||||
this.subscribe(this.plugin.behaviors.state.isBusy, busy => {
|
||||
if (this.busy.value !== busy) this.busy.next(busy);
|
||||
});
|
||||
this.subscribe(this.busy, busy => {
|
||||
if (this.state.busy !== busy) this.setState({ busy });
|
||||
this.subscribe(this.busy.pipe(skip(1)), busy => {
|
||||
this.setState({ busy });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,32 +4,39 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from './base';
|
||||
import { PluginReactContext, PluginUIComponent } from './base';
|
||||
import { OrderedMap } from 'immutable';
|
||||
import { TaskManager } from '../mol-plugin/util/task-manager';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { Progress } from '../mol-task';
|
||||
import { IconButton } from './controls/common';
|
||||
import { CancelSvg } from './controls/icons';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
|
||||
export class BackgroundTaskProgress extends PluginUIComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
|
||||
componentDidMount() {
|
||||
const hideOverlay = !!this.plugin.spec.components?.hideTaskOverlay;
|
||||
this.subscribe(this.plugin.events.task.progress.pipe(filter(e => e.level === 'background' && (hideOverlay || !e.useOverlay))), e => {
|
||||
this.setState({ tracked: this.state.tracked.set(e.id, e) });
|
||||
export function BackgroundTaskProgress() {
|
||||
const plugin = useContext(PluginReactContext);
|
||||
const [tracked, setTracked] = useState<OrderedMap<number, TaskManager.ProgressEvent>>(OrderedMap());
|
||||
|
||||
useEffect(() => {
|
||||
const started = plugin.events.task.progress.subscribe(e => {
|
||||
const hideOverlay = !!plugin.spec.components?.hideTaskOverlay;
|
||||
if (e.level === 'background' && (hideOverlay || !e.useOverlay)) {
|
||||
setTracked(tracked => tracked.set(e.id, e));
|
||||
}
|
||||
});
|
||||
this.subscribe(this.plugin.events.task.finished, ({ id }) => {
|
||||
this.setState({ tracked: this.state.tracked.delete(id) });
|
||||
|
||||
const finished = plugin.events.task.finished.subscribe(({ id }) => {
|
||||
setTracked(tracked => tracked.delete(id));
|
||||
});
|
||||
}
|
||||
|
||||
state = { tracked: OrderedMap<number, TaskManager.ProgressEvent>() };
|
||||
return () => {
|
||||
started.unsubscribe();
|
||||
finished.unsubscribe();
|
||||
};
|
||||
}, [plugin]);
|
||||
|
||||
render() {
|
||||
return <div className='msp-background-tasks'>
|
||||
{this.state.tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
|
||||
</div>;
|
||||
}
|
||||
return <div className='msp-background-tasks'>
|
||||
{tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
class ProgressEntry extends PluginUIComponent<{ event: TaskManager.ProgressEvent }> {
|
||||
@@ -65,23 +72,30 @@ function countSubtasks(progress: Progress.Node) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
export class OverlayTaskProgress extends PluginUIComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
|
||||
componentDidMount() {
|
||||
this.subscribe(this.plugin.events.task.progress.pipe(filter(e => !!e.useOverlay)), e => {
|
||||
this.setState({ tracked: this.state.tracked.set(e.id, e) });
|
||||
export function OverlayTaskProgress() {
|
||||
const plugin = useContext(PluginReactContext);
|
||||
const [tracked, setTracked] = useState<OrderedMap<number, TaskManager.ProgressEvent>>(OrderedMap());
|
||||
|
||||
useEffect(() => {
|
||||
const started = plugin.events.task.progress.subscribe(e => {
|
||||
if (!!e.useOverlay) {
|
||||
setTracked(tracked => tracked.set(e.id, e));
|
||||
}
|
||||
});
|
||||
this.subscribe(this.plugin.events.task.finished, ({ id }) => {
|
||||
this.setState({ tracked: this.state.tracked.delete(id) });
|
||||
|
||||
const finished = plugin.events.task.finished.subscribe(({ id }) => {
|
||||
setTracked(tracked => tracked.delete(id));
|
||||
});
|
||||
}
|
||||
|
||||
state = { tracked: OrderedMap<number, TaskManager.ProgressEvent>() };
|
||||
return () => {
|
||||
started.unsubscribe();
|
||||
finished.unsubscribe();
|
||||
};
|
||||
}, [plugin]);
|
||||
|
||||
render() {
|
||||
if (this.state.tracked.size === 0) return null;
|
||||
if (tracked.size === 0) return null;
|
||||
|
||||
return <div className='msp-overlay-tasks'>
|
||||
{this.state.tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
return <div className='msp-overlay-tasks'>
|
||||
{tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export class BindingsHelp extends React.PureComponent<{ bindings: { [k: string]:
|
||||
}
|
||||
}
|
||||
|
||||
export class HelpText extends React.PureComponent {
|
||||
export class HelpText extends React.PureComponent<{ children?: any }> {
|
||||
render() {
|
||||
return <div className='msp-help-text'>
|
||||
<div>{this.props.children}</div>
|
||||
@@ -45,7 +45,7 @@ export class HelpText extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export class HelpGroup extends React.PureComponent<{ header: string, initiallyExpanded?: boolean }, { isExpanded: boolean }> {
|
||||
export class HelpGroup extends React.PureComponent<{ children?: any, header: string, initiallyExpanded?: boolean }, { isExpanded: boolean }> {
|
||||
state = {
|
||||
header: this.props.header,
|
||||
isExpanded: !!this.props.initiallyExpanded
|
||||
|
||||
@@ -80,7 +80,6 @@ export async function getContourLevelEmdb(plugin: PluginContext, taskCtx: Runtim
|
||||
}
|
||||
}
|
||||
const contourLevel = parseFloat(primaryContour.getElementsByTagName('level')[0].textContent!);
|
||||
|
||||
return contourLevel;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface QualityProps {
|
||||
doubleSided: boolean
|
||||
xrayShaded: boolean
|
||||
alpha: number
|
||||
allowTransparentBackfaces: boolean
|
||||
transparentBackfaces: 'off' | 'on' | 'opaque'
|
||||
}
|
||||
|
||||
export const DefaultQualityThresholds = {
|
||||
@@ -193,7 +193,7 @@ export function getQualityProps(props: Partial<QualityProps>, data?: any) {
|
||||
resolution = Math.max(resolution, volume / 500_000_000);
|
||||
resolution = Math.min(resolution, 20);
|
||||
|
||||
if (!props.allowTransparentBackfaces && ((props.alpha !== undefined && props.alpha < 1) || !!props.xrayShaded)) {
|
||||
if (props.transparentBackfaces === 'off' && ((props.alpha !== undefined && props.alpha < 1) || !!props.xrayShaded)) {
|
||||
doubleSided = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -35,17 +35,25 @@ import { OperatorHklColorThemeProvider } from './color/operator-hkl';
|
||||
import { PartialChargeColorThemeProvider } from './color/partial-charge';
|
||||
import { AtomIdColorThemeProvider } from './color/atom-id';
|
||||
import { EntityIdColorThemeProvider } from './color/entity-id';
|
||||
import { TextureFilter } from '../mol-gl/webgl/texture';
|
||||
import { Texture, TextureFilter } from '../mol-gl/webgl/texture';
|
||||
import { VolumeValueColorThemeProvider } from './color/volume-value';
|
||||
import { Vec3, Vec4 } from '../mol-math/linear-algebra';
|
||||
|
||||
export type LocationColor = (location: Location, isSecondary: boolean) => Color
|
||||
|
||||
export interface ColorVolume {
|
||||
colors: Texture
|
||||
dimension: Vec3
|
||||
transform: Vec4
|
||||
}
|
||||
|
||||
export { ColorTheme };
|
||||
interface ColorTheme<P extends PD.Params> {
|
||||
readonly factory: ColorTheme.Factory<P>
|
||||
readonly granularity: ColorType
|
||||
readonly color: LocationColor
|
||||
readonly props: Readonly<PD.Values<P>>
|
||||
readonly grid?: ColorVolume
|
||||
/**
|
||||
* if palette is defined, 24bit RGB color value normalized to interval [0, 1]
|
||||
* is used as index to the colors
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# 0.9.9
|
||||
* /ligand queries: fix behavior for alternate locations
|
||||
* /ligand queries: handle additional atoms more gracefully
|
||||
* /ligand queries: better error message for UNL
|
||||
* /ligand queries: treat deuterium/tritium as hydrogen
|
||||
|
||||
# 0.9.8
|
||||
* fix support for chem_comp_bond and struct_conn categories
|
||||
|
||||
|
||||
@@ -237,10 +237,8 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
|
||||
encoder.writeCategory(_model_server_params, entry);
|
||||
|
||||
if (entry.queryDefinition.niceName === 'Ligand') {
|
||||
if (encoder instanceof MolEncoder) {
|
||||
encoder.setComponentAtomData(ComponentAtom.Provider.get(structure.models[0])!);
|
||||
}
|
||||
if (encoder instanceof MolEncoder || encoder instanceof Mol2Encoder) {
|
||||
encoder.setComponentAtomData(ComponentAtom.Provider.get(structure.models[0])!);
|
||||
encoder.setComponentBondData(ComponentBond.Provider.get(structure.models[0])!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
export const VERSION = '0.9.8';
|
||||
export const VERSION = '0.9.9';
|
||||
Reference in New Issue
Block a user