Compare commits

...

32 Commits

Author SHA1 Message Date
dsehnal
805f772696 3.0.0-dev.5 2021-12-16 12:21:45 +01:00
dsehnal
308bbc1ea0 lint 2021-12-16 12:19:32 +01:00
dsehnal
4a248b5591 shouldResetCamera reset fix 2021-12-16 12:17:46 +01:00
David Sehnal
704a9a111d Merge pull request #316 from JonStargaryen/master
update State Snapshots canApply when snapshots are added
2021-12-16 11:48:15 +01:00
JonStargaryen
a6befc5509 update State Snapshots canApply when snapshots are added 2021-12-16 11:31:53 +01:00
dsehnal
9e7aa4226d CI task 2021-12-14 21:55:23 +01:00
David Sehnal
7a25699c23 Merge pull request #312 from molstar/dependabot/npm_and_yarn/swagger-ui-dist-4.1.3
Bump swagger-ui-dist from 4.1.1 to 4.1.3
2021-12-14 14:29:01 +01:00
dependabot[bot]
901ae7f6d6 Bump swagger-ui-dist from 4.1.1 to 4.1.3
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 4.1.1 to 4.1.3.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v4.1.1...v4.1.3)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-14 13:26:49 +00:00
dsehnal
c57a4cdf6e 3.0.0-dev.4 2021-12-14 14:25:51 +01:00
dsehnal
18573e17f0 package.lock 2021-12-14 14:23:37 +01:00
dsehnal
5bc7ffa8a7 react and react-dom as peerDependencies 2021-12-14 14:17:25 +01:00
David Sehnal
430cc83259 Merge pull request #311 from molstar/upload-overlay
Drag and Drop Overlay
2021-12-14 14:11:15 +01:00
dsehnal
3cd3afb775 drag and drop overlay 2021-12-14 14:10:05 +01:00
dsehnal
93215b6beb prefer webgl 1 in Safari 15.1-15.3 2021-12-14 13:19:27 +01:00
Alexander Rose
e4f630dbef improve drag and drop support
- any file type
- multiple files
- if session, only first is loaded
2021-12-11 16:37:20 -08:00
Alexander Rose
ccaf18af04 add analytics to viewer on deployment
- only for molstar.org (done in ./scripts/deploy.js)
2021-12-11 12:25:05 -08:00
Alexander Rose
2b72098f95 fix: false positives in Model.isFromPdbArchive
- wrong for, e.g., AlphaFold DB entries
2021-12-11 11:42:15 -08:00
Alexander Rose
b32546bea7 add outline color option to renderer 2021-12-11 11:30:00 -08:00
Alexander Rose
b9b0413e9f improve label repr defaults 2021-12-11 11:25:35 -08:00
David Sehnal
1d29b4627f Merge pull request #305 from MadCatX/cleanup-old-nodejs
Make cleanup script work with older vesions of Node.js
2021-12-10 12:43:08 +01:00
Michal Malý
bd44c76709 Make cleanup script work with older versions of Node.js 2021-12-10 09:36:58 +01:00
Alexander Rose
06b4761f2b add `carbonLightness` to illustrative color theme 2021-12-06 20:02:25 -08:00
Alexander Rose
daa3d1dbaa handle zero light count 2021-12-06 20:01:46 -08:00
Alexander Rose
5490d5ceb5 fixes for material bumpiness 2021-12-06 20:01:18 -08:00
dsehnal
cf3c1cfcce update Group Presets UI 2021-12-06 10:44:20 +01:00
Alexander Rose
be2607ae84 add bumpAmplitude render parameter 2021-12-05 21:29:07 -08:00
Alexander Rose
e2966241e8 Merge pull request #299 from molstar/bump
procedural bump mapping
2021-12-05 12:42:27 -08:00
Alexander Rose
447792b1ef changelog 2021-12-05 12:30:28 -08:00
Alexander Rose
1cbcb5c530 remove duplicated line 2021-12-05 12:10:26 -08:00
Alexander Rose
f8d32d1d8d fix temp texture usage for color smoothing 2021-12-05 12:07:32 -08:00
Alexander Rose
8c556c2849 Merge branch 'master' of https://github.com/molstar/molstar into bump 2021-12-05 12:02:21 -08:00
Alexander Rose
98050875c7 add bumpiness to material 2021-12-04 16:54:23 -08:00
57 changed files with 452 additions and 120 deletions

View File

@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14
node-version: 17
- run: npm ci
- run: sudo apt-get install xvfb
- name: Lint

View File

@@ -6,6 +6,22 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v3.0.0-dev.5] - 2021-12-16
- Fix initial camera reset not triggering for some entries.
## [v3.0.0-dev.4] - 2021-12-14
- Add ``bumpiness`` (per-object and per-group), ``bumpFrequency`` & ``bumpAmplitude`` (per-object) render parameters (#299)
- Change ``label`` representation defaults: Use text border instead of rectangle background
- Add outline color option to renderer
- Fix false positives in Model.isFromPdbArchive
- Add drag and drop support for loading any file, including multiple at once
- If there are session files (.molx or .molj) among the dropped files, only the first session will be loaded
- Add drag and drop overlay
- Safari 15.1 - 15.3 WebGL 2 support workaround
- [Breaking] Move ``react`` and ``react-dom`` to ``peerDependencies``. This might break some builds.
## [v3.0.0-dev.3] - 2021-12-4
- Fix OBJ and USDZ export

28
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "molstar",
"version": "3.0.0-dev.3",
"version": "3.0.0-dev.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "molstar",
"version": "3.0.0-dev.3",
"version": "3.0.0-dev.5",
"license": "MIT",
"dependencies": {
"@types/argparse": "^2.0.10",
@@ -27,8 +27,6 @@
"immer": "^9.0.7",
"immutable": "^3.8.2",
"node-fetch": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rxjs": "^7.4.0",
"swagger-ui-dist": "^4.1.1",
"tslib": "^2.3.1",
@@ -85,6 +83,10 @@
},
"optionalDependencies": {
"gl": "^4.9.2"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
},
"node_modules/@babel/code-frame": {
@@ -11292,6 +11294,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -11304,6 +11307,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -11864,6 +11868,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -12522,9 +12527,9 @@
}
},
"node_modules/swagger-ui-dist": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.1.tgz",
"integrity": "sha512-xvPMEpOCbd5S9Z1pJT/L/K/C2yAX89ZWJDxEECkVW1dENsKFLbu7sRY7b6UBqLXuTUfdM5owlgPrpwr24CRonA=="
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.3.tgz",
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
},
"node_modules/swap-case": {
"version": "2.0.2",
@@ -22606,6 +22611,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -22615,6 +22621,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -23039,6 +23046,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -23553,9 +23561,9 @@
}
},
"swagger-ui-dist": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.1.tgz",
"integrity": "sha512-xvPMEpOCbd5S9Z1pJT/L/K/C2yAX89ZWJDxEECkVW1dENsKFLbu7sRY7b6UBqLXuTUfdM5owlgPrpwr24CRonA=="
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.3.tgz",
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
},
"swap-case": {
"version": "2.0.2",

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.0.0-dev.3",
"version": "3.0.0-dev.5",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -146,14 +146,16 @@
"immer": "^9.0.7",
"immutable": "^3.8.2",
"node-fetch": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rxjs": "^7.4.0",
"swagger-ui-dist": "^4.1.1",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"optionalDependencies": {
"gl": "^4.9.2"
}

View File

@@ -7,6 +7,23 @@
const fs = require('fs');
const path = require('path');
function removeDir(dirPath) {
for (const ent of fs.readdirSync(dirPath)) {
const entryPath = path.join(dirPath, ent);
remove(entryPath);
}
fs.rmdirSync(dirPath);
}
function remove(entryPath) {
const st = fs.statSync(entryPath);
if (st.isDirectory())
removeDir(entryPath);
else
fs.unlinkSync(entryPath);
}
const toClean = [
path.resolve(__dirname, '../build'),
path.resolve(__dirname, '../lib'),
@@ -14,6 +31,11 @@ const toClean = [
];
toClean.forEach(ph => {
if (fs.existsSync(ph))
fs.rm(ph, { recursive: true }, err => { if (err) console.warn(`Failed to delete ${ph}: ${err}`); });
if (fs.existsSync(ph)) {
try {
remove(ph);
} catch (err) {
console.warn(`Cleanup failed: ${err}`);
}
}
});

View File

@@ -14,6 +14,9 @@ const buildDir = path.resolve(__dirname, '../build/');
const deployDir = path.resolve(buildDir, 'deploy/');
const localPath = path.resolve(deployDir, 'molstar.github.io/');
const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g;
const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics -->`;
function log(command, stdout, stderr) {
if (command) {
console.log('\n###', command);
@@ -22,11 +25,18 @@ function log(command, stdout, stderr) {
}
}
function addAnalytics(path) {
const data = fs.readFileSync(path, 'utf8');
const result = data.replace(analyticsTag, analyticsCode);
fs.writeFileSync(path, result, 'utf8');
}
function copyViewer() {
console.log('\n###', 'copy viewer files');
const viewerBuildPath = path.resolve(buildDir, '../build/viewer/');
const viewerDeployPath = path.resolve(localPath, 'viewer/');
fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true });
addAnalytics(path.resolve(viewerDeployPath, 'index.html'));
}
if (!fs.existsSync(localPath)) {

View File

@@ -51,7 +51,8 @@ function occlusionStyle(plugin: PluginContext) {
} },
outline: { name: 'on', params: {
scale: 1.0,
threshold: 0.33
threshold: 0.33,
color: Color(0x0000),
} }
}
} });

View File

@@ -91,5 +91,6 @@
var emdb = getParam('emdb', '[^&]+').trim();
if (emdb) viewer.loadEmdb(emdb);
</script>
<!-- __MOLSTAR_ANALYTICS__ -->
</body>
</html>

View File

@@ -11,6 +11,7 @@ import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
@@ -26,7 +27,7 @@ const Canvas3DPresets = {
},
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
outline: { name: 'on', params: { scale: 1, threshold: 0.1 } }
outline: { name: 'on', params: { scale: 1, threshold: 0.1, color: Color(0x000000) } }
},
renderer: {
style: { name: 'flat', params: {} }

View File

@@ -125,17 +125,20 @@ export class Mp4Controls extends PluginComponent {
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
this.subscribe(this.plugin.behaviors.state.isBusy, b => {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
});
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));
this.subscribe(this.plugin.managers.snapshot.events.changed, b => this.updateCanApply(b));
this.sync();
this.syncInfo();
}
private updateCanApply(b?: any) {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
}
constructor(private plugin: PluginContext) {
super();

View File

@@ -514,7 +514,7 @@ namespace Canvas3D {
if (camera.transition.inTransition || nextCameraResetSnapshot) return false;
let cameraSphereOverlapsNone = true;
let cameraSphereOverlapsNone = true, isEmpty = true;
Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius);
// check if any renderable has moved outside of the old bounding sphere
@@ -525,12 +525,13 @@ namespace Canvas3D {
const b = r.values.boundingSphere.ref.value;
if (!b.radius) continue;
isEmpty = false;
const cameraDist = Vec3.distance(cameraSphere.center, b.center);
if ((cameraDist > cameraSphere.radius || cameraDist > b.radius || b.radius > camera.state.radiusMax) && !Sphere3D.includes(oldBoundingSphereVisible, b)) return true;
if (Sphere3D.overlaps(cameraSphere, b)) cameraSphereOverlapsNone = false;
}
return cameraSphereOverlapsNone;
return cameraSphereOverlapsNone || (!isEmpty && cameraSphere.radius <= 0.1);
}
const sceneCommitTimeoutMs = 250;

View File

@@ -193,6 +193,7 @@ const PostprocessingSchema = {
uFogNear: UniformSpec('f'),
uFogFar: UniformSpec('f'),
uFogColor: UniformSpec('v3'),
uOutlineColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('b'),
uMaxPossibleViewZDiff: UniformSpec('f'),
@@ -220,6 +221,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uFogNear: ValueCell.create(10000),
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
uOutlineColor: ValueCell.create(Vec3.create(0, 0, 0)),
uTransparentBackground: ValueCell.create(false),
uMaxPossibleViewZDiff: ValueCell.create(0.5),
@@ -252,6 +254,7 @@ export const PostprocessingParams = {
on: PD.Group({
scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }),
threshold: PD.Numeric(0.33, { min: 0.01, max: 1, step: 0.01 }),
color: PD.Color(Color(0x000000)),
}),
off: PD.Group({})
}, { cycle: true, description: 'Draw outline around 3D objects' }),
@@ -446,6 +449,8 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.outlinesRenderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
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);
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale);

View File

@@ -105,6 +105,7 @@ export namespace BaseGeometry {
drawCount: ValueCell.create(counts.drawCount),
uMetalness: ValueCell.create(props.material.metalness),
uRoughness: ValueCell.create(props.material.roughness),
uBumpiness: ValueCell.create(props.material.bumpiness),
dLightCount: ValueCell.create(1),
};
}
@@ -113,6 +114,7 @@ export namespace BaseGeometry {
ValueCell.updateIfChanged(values.alpha, props.alpha); // `uAlpha` is set in renderable.render
ValueCell.updateIfChanged(values.uMetalness, props.material.metalness);
ValueCell.updateIfChanged(values.uRoughness, props.material.roughness);
ValueCell.updateIfChanged(values.uBumpiness, props.material.bumpiness);
}
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {

View File

@@ -157,6 +157,8 @@ export namespace Cylinders {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -237,6 +239,8 @@ export namespace Cylinders {
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
}
@@ -252,6 +256,8 @@ export namespace Cylinders {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {

View File

@@ -376,7 +376,7 @@ export function applyMeshSubstanceSmoothing(values: MeshValues, resolution: numb
colorType: values.dSubstanceType.ref.value,
boundingSphere: values.boundingSphere.ref.value,
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
itemSize: 3
itemSize: 4
}, resolution, stride, webgl, colorTexture);
if (smoothingData.kind === 'volume') {
ValueCell.updateIfChanged(values.dSubstanceType, smoothingData.type);

View File

@@ -625,6 +625,8 @@ export namespace Mesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -696,6 +698,8 @@ export namespace Mesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
meta: ValueCell.create(mesh.meta),
};
@@ -714,6 +718,8 @@ export namespace Mesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {

View File

@@ -129,6 +129,8 @@ export namespace Spheres {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -204,6 +206,8 @@ export namespace Spheres {
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
}
@@ -219,6 +223,8 @@ export namespace Spheres {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {

View File

@@ -23,14 +23,14 @@ export type SubstanceData = {
export function applySubstanceMaterial(array: Uint8Array, start: number, end: number, material: Material) {
for (let i = start; i < end; ++i) {
Material.toArray(material, array, i * 3);
array[i * 3 + 2] = 255;
Material.toArray(material, array, i * 4);
array[i * 4 + 3] = 255;
}
return true;
}
export function clearSubstance(array: Uint8Array, start: number, end: number) {
array.fill(0, start * 3, end * 3);
array.fill(0, start * 4, end * 4);
return true;
}

View File

@@ -510,10 +510,10 @@ export function applyTextureMeshSubstanceSmoothing(values: TextureMeshValues, re
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
if (!webgl.namedTextures[ColorSmoothingRgbName]) {
webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
}
const colorData = webgl.namedTextures[ColorSmoothingRgbName];
const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
colorData.load(values.tSubstance.ref.value);
const smoothingData = calcTextureMeshColorSmoothing({

View File

@@ -113,6 +113,8 @@ export namespace TextureMesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -168,6 +170,8 @@ export namespace TextureMesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
dGeoTexture: ValueCell.create(true),
meta: ValueCell.create(textureMesh.meta),
@@ -187,6 +191,8 @@ export namespace TextureMesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { CylindersShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -26,6 +26,8 @@ export const CylindersSchema = {
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};
export type CylindersSchema = typeof CylindersSchema
export type CylindersValues = Values<CylindersSchema>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec, UniformSpec } from './schema';
import { MeshShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -22,6 +22,8 @@ export const MeshSchema = {
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')
} as const;
export type MeshSchema = typeof MeshSchema

View File

@@ -243,12 +243,12 @@ export type TransparencyValues = Values<TransparencySchema>
export const SubstanceSchema = {
uSubstanceTexDim: UniformSpec('v2'),
tSubstance: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
tSubstance: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
dSubstance: DefineSpec('boolean'),
uSubstanceGridDim: UniformSpec('v3'),
uSubstanceGridTransform: UniformSpec('v4'),
tSubstanceGrid: TextureSpec('texture', 'rgb', 'ubyte', 'linear'),
tSubstanceGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
dSubstanceType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
} as const;
export type SubstanceSchema = typeof SubstanceSchema
@@ -288,6 +288,7 @@ export const BaseSchema = {
uAlpha: UniformSpec('f', 'material'),
uMetalness: UniformSpec('f', 'material'),
uRoughness: UniformSpec('f', 'material'),
uBumpiness: UniformSpec('f', 'material'),
uVertexCount: UniformSpec('i'),
uInstanceCount: UniformSpec('i'),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { SpheresShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -23,6 +23,8 @@ export const SpheresSchema = {
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};
export type SpheresSchema = typeof SpheresSchema
export type SpheresValues = Values<SpheresSchema>

View File

@@ -24,6 +24,8 @@ export const TextureMeshSchema = {
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dGeoTexture: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')
};
export type TextureMeshSchema = typeof TextureMeshSchema

View File

@@ -8,17 +8,16 @@
*/
export const apply_light_color = `
// inputs
// - vec4 material
// - vec3 vViewPosition
// - vec3 normal
// - float uMetalness
// - float uRoughness
// - vec3 uLightColor
// - vec3 uAmbientColor
// outputs
// - sets gl_FragColor
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!any(isNaN(bumpNormal))) normal = bumpNormal;
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
@@ -26,7 +25,13 @@ ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0),
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
physicalMaterial.roughness = max(roughness, 0.0525);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;

View File

@@ -44,16 +44,16 @@ export const assign_color_varying = `
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance)
vSubstance = readFromTexture(tSubstance, aInstance * float(uGroupCount) + group, uSubstanceTexDim).rgb;
vSubstance = readFromTexture(tSubstance, aInstance * float(uGroupCount) + group, uSubstanceTexDim);
#elif defined(dSubstanceType_vertexInstance)
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim).rgb;
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim);
#elif defined(dSubstanceType_volumeInstance)
vec3 sgridPos = (uSubstanceGridTransform.w * (vModelPosition - uSubstanceGridTransform.xyz)) / uSubstanceGridDim;
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim).rgb;
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim);
#endif
// pre-mix to avoid artifacts due to empty substance
vSubstance.rg = mix(vec2(uMetalness, uRoughness), vSubstance.rg, vSubstance.b);
vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
#if defined(dRenderVariant_pickObject)

View File

@@ -23,9 +23,11 @@ export const assign_material_color = `
float metalness = uMetalness;
float roughness = uRoughness;
float bumpiness = uBumpiness;
#ifdef dSubstance
metalness = mix(metalness, vSubstance.r, vSubstance.b);
roughness = mix(roughness, vSubstance.g, vSubstance.b);
metalness = mix(metalness, vSubstance.r, vSubstance.a);
roughness = mix(roughness, vSubstance.g, vSubstance.a);
bumpiness = mix(bumpiness, vSubstance.b, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
vec4 material = vColor;

View File

@@ -1,6 +1,11 @@
export const color_frag_params = `
uniform float uMetalness;
uniform float uRoughness;
uniform float uBumpiness;
#ifdef bumpEnabled
uniform float uBumpFrequency;
uniform float uBumpAmplitude;
#endif
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
@@ -14,7 +19,7 @@ uniform float uRoughness;
#endif
#ifdef dSubstance
varying vec3 vSubstance;
varying vec4 vSubstance;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ == 100

View File

@@ -1,6 +1,7 @@
export const color_vert_params = `
uniform float uMetalness;
uniform float uRoughness;
uniform float uBumpiness;
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
@@ -36,11 +37,11 @@ uniform float uRoughness;
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
varying vec3 vSubstance;
varying vec4 vSubstance;
uniform vec2 uSubstanceTexDim;
uniform sampler2D tSubstance;
#elif defined(dSubstanceType_volumeInstance)
varying vec3 vSubstance;
varying vec4 vSubstance;
uniform vec2 uSubstanceTexDim;
uniform vec3 uSubstanceGridDim;
uniform vec4 uSubstanceGridTransform;

View File

@@ -76,4 +76,49 @@ float calcDepth(const in vec3 pos) {
vec2 clipZW = pos.z * uProjection[2].zw + uProjection[3].zw;
return 0.5 + 0.5 * clipZW.x / clipZW.y;
}
// "Bump Mapping Unparametrized Surfaces on the GPU" Morten S. Mikkelsen
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
vec3 perturbNormal(in vec3 position, in vec3 normal, in float height, in float scale) {
vec3 sigmaS = dFdx(position);
vec3 sigmaT = dFdy(position);
vec3 r1 = cross(sigmaT, normal);
vec3 r2 = cross(normal, sigmaS);
float det = dot(sigmaS, r1);
float bs = dFdx(height);
float bt = dFdy(height);
vec3 surfGrad = sign(det) * (bs * r1 + bt * r2);
return normalize(abs(det) * normal - scale * surfGrad);
}
float hash(in float h) {
return fract(sin(h) * 43758.5453123);
}
float noise(in vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 157.0 + 113.0 * p.z;
return mix(
mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 157.0), hash(n + 158.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 270.0), hash(n + 271.0), f.x), f.y), f.z);
}
float fbm(in vec3 p) {
float f = 0.0;
f += 0.5 * noise(p);
p *= 2.01;
f += 0.25 * noise(p);
p *= 2.02;
f += 0.125 * noise(p);
return f;
}
`;

View File

@@ -214,6 +214,9 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
a31 * b01 - a30 * b03 - a32 * b00,
a20 * b03 - a21 * b01 + a22 * b00) / det;
}
#define isNaN(x) ( (x) != (x) )
#define isInf(x) ( (x) == (x)+1. )
#else
#define transpose2(m) transpose(m)
#define transpose3(m) transpose(m)
@@ -222,5 +225,8 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
#define inverse2(m) inverse(m)
#define inverse3(m) inverse(m)
#define inverse4(m) inverse(m)
#define isNaN isnan
#define isInf isinf
#endif
`;

View File

@@ -8,8 +8,10 @@
*/
export const light_frag_params = `
uniform vec3 uLightDirection[dLightCount];
uniform vec3 uLightColor[dLightCount];
#if dLightCount != 0
uniform vec3 uLightDirection[dLightCount];
uniform vec3 uLightColor[dLightCount];
#endif
uniform vec3 uAmbientColor;
struct PhysicalMaterial {

View File

@@ -8,6 +8,8 @@ export const cylinders_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
uniform mat4 uView;
varying mat4 vTransform;
@@ -18,6 +20,7 @@ varying float vCap;
uniform vec3 uCameraDir;
uniform vec3 uCameraPosition;
uniform mat4 uInvView;
#include common
#include common_frag_params
@@ -108,7 +111,8 @@ void main() {
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
gl_FragDepthEXT = calcDepth(vViewPosition);
// bugfix (mac only?)
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;

View File

@@ -8,6 +8,8 @@ export const mesh_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
#include common
#include common_frag_params
#include color_frag_params

View File

@@ -21,6 +21,7 @@ uniform float uFar;
uniform float uFogNear;
uniform float uFogFar;
uniform vec3 uFogColor;
uniform vec3 uOutlineColor;
uniform bool uTransparentBackground;
uniform float uOcclusionBias;
@@ -116,7 +117,7 @@ void main(void) {
float outline = getOutline(coords, closestTexel);
if (outline == 0.0) {
color.rgb *= outline;
color.rgb = mix(uOutlineColor, color.rgb, outline);
viewDist = abs(getViewZ(closestTexel));
fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!uTransparentBackground) {

View File

@@ -8,12 +8,16 @@ export const spheres_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
#include common
#include common_frag_params
#include color_frag_params
#include light_frag_params
#include common_clip
uniform mat4 uInvView;
varying float vRadius;
varying float vRadiusSq;
varying vec3 vPoint;
@@ -73,7 +77,8 @@ void main(void){
gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius);
}
// bugfix (mac only?)
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;

View File

@@ -283,8 +283,10 @@ export namespace Model {
export function isFromPdbArchive(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
for (let i = 0, il = db.database_2.database_id.rowCount; i < il; ++i) {
if (db.database_2.database_id.value(i) === 'PDB') return true;
}
return (
db.database_2.database_id.isDefined ||
// 4 character PDB id
model.entryId.match(/^[1-9][a-z0-9]{3,3}$/i) !== null ||
// long PDB id

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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,9 @@
import * as React from 'react';
import { Mat4, Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorListEntry } from '../../mol-util/color/color';
import { ColorListName, ColorListOptions, ColorListOptionsScale, ColorListOptionsSet, getColorListFromName } from '../../mol-util/color/lists';
import { Legend as LegendData } from '../../mol-util/legend';
import { memoize1, memoizeLatest } from '../../mol-util/memoize';
@@ -16,16 +18,14 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ParamMapping } from '../../mol-util/param-mapping';
import { camelCaseToWords } from '../../mol-util/string';
import { PluginUIComponent } from '../base';
import { PluginUIContext } from '../context';
import { ActionMenu } from './action-menu';
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
import { Button, ControlGroup, ControlRow, ExpandGroup, IconButton, TextInput, ToggleButton } from './common';
import { Icon, HelpOutlineSvg, CheckSvg, ClearSvg, BookmarksOutlinedSvg, MoreHorizSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg, TuneSvg } from './icons';
import { ArrowDownwardSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowUpwardSvg, BookmarksOutlinedSvg, CheckSvg, ClearSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg } from './icons';
import { legendFor } from './legend';
import { LineGraphComponent } from './line-graph/line-graph-component';
import { Slider, Slider2 } from './slider';
import { Asset } from '../../mol-util/assets';
import { ColorListEntry } from '../../mol-util/color/color';
import { PluginUIContext } from '../context';
export type ParameterControlsCategoryFilter = string | null | (string | null)[]
@@ -1100,14 +1100,14 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
this.change(item?.value);
}
presets() {
pivotedPresets() {
if (!this.props.param.presets) return null;
const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div className='msp-control-group-wrapper'>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleShowPresets}>
<Icon svg={TuneSvg} />
<Icon svg={BookmarksOutlinedSvg} />
{label} Presets
</button>
</div>
@@ -1115,6 +1115,22 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
</div>;
}
presets() {
if (!this.props.param.presets) return null;
return <>
<div className='msp-control-group-presets-wrapper'>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleShowPresets}>
<Icon svg={BookmarksOutlinedSvg} />
Presets
</button>
</div>
</div>
{this.state.showPresets && <ActionMenu items={this.presetItems(this.props.param)} onSelect={this.onSelectPreset} />}
</>;
}
pivoted() {
const key = this.props.param.pivot as string;
const params = this.props.param.params;
@@ -1139,7 +1155,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
{ctrl}
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
<div className='msp-control-offset'>
{this.presets()}
{this.pivotedPresets()}
<ParameterControls params={filtered} onEnter={this.props.onEnter} values={this.props.value} onChange={this.onChangeParam} isDisabled={this.props.isDisabled} />
</div>
</div>;
@@ -1165,15 +1181,15 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
return controls;
}
return <div className='msp-control-group-wrapper'>
return <div className='msp-control-group-wrapper' style={{ position: 'relative' }}>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleExpanded}>
<Icon svg={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} />
{label}
</button>
</div>
{this.presets()}
{this.state.isExpanded && <div className='msp-control-offset'>
{this.presets()}
{controls}
</div>}
</div>;

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-21 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
interface Behavior<T> {
value: T;
@@ -16,26 +16,20 @@ export function useBehavior<T>(s: Behavior<T>): T;
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 [value, setValue] = useState(s?.value);
const [, next] = useState({});
const current = useRef<T>();
current.current = s?.value;
useEffect(() => {
if (!s) {
if (value !== void 0) setValue(void 0);
return;
}
let fst = true;
const sub = s.subscribe((v) => {
if (fst) {
fst = false;
if (v !== value) setValue(v);
} else setValue(v);
if (current.current !== v) next({});
});
return () => {
sub.unsubscribe();
};
// eslint-disable-next-line
return () => sub.unsubscribe();
}, [s]);
return value;
return s?.value;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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>
@@ -18,6 +18,10 @@ import { Toasts } from './toast';
import { Viewport, ViewportControls } from './viewport';
import { PluginCommands } from '../mol-plugin/commands';
import { PluginUIContext } from './context';
import { OpenFiles } from '../mol-plugin-state/actions/file';
import { Asset } from '../mol-util/assets';
import { BehaviorSubject } from 'rxjs';
import { useBehavior } from './hooks/use-behavior';
export class Plugin extends React.Component<{ plugin: PluginUIContext }, {}> {
region(kind: 'left' | 'right' | 'bottom' | 'main', element: JSX.Element) {
@@ -102,22 +106,34 @@ class Layout extends PluginUIComponent {
onDrop = (ev: React.DragEvent<HTMLDivElement>) => {
ev.preventDefault();
let file: File | undefined | null;
const files: File[] = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file') continue;
file = ev.dataTransfer.items[i].getAsFile();
break;
const file = ev.dataTransfer.items[i].getAsFile();
if (file) files.push(file);
}
} else {
file = ev.dataTransfer.files[0];
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[0];
if (file) files.push(file);
}
}
if (!file) return;
const fn = file?.name.toLowerCase() || '';
if (fn.endsWith('molx') || fn.endsWith('molj')) {
PluginCommands.State.Snapshots.OpenFile(this.plugin, { file });
const sessions = files.filter(f => {
const fn = f.name.toLowerCase();
return fn.endsWith('.molx') || fn.endsWith('.molj');
});
if (sessions.length > 0) {
PluginCommands.State.Snapshots.OpenFile(this.plugin, { file: sessions[0] });
} else {
this.plugin.runTask(this.plugin.state.data.applyAction(OpenFiles, {
files: files.map(f => Asset.File(f)),
format: { name: 'auto', params: {} },
visuals: true
}));
}
}
@@ -125,13 +141,16 @@ class Layout extends PluginUIComponent {
ev.preventDefault();
}
private showDragOverlay = new BehaviorSubject(false);
onDragEnter = () => this.showDragOverlay.next(true);
render() {
const layout = this.plugin.layout.state;
const controls = this.plugin.spec.components?.controls || {};
const viewport = this.plugin.spec.components?.viewport?.view || DefaultViewport;
return <div className='msp-plugin' onDrop={this.onDrop} onDragOver={this.onDragOver}>
<div className={this.layoutClassName}>
return <div className='msp-plugin'>
<div className={this.layoutClassName} onDragEnter={this.onDragEnter}>
<div className={this.layoutVisibilityClassName}>
{this.region('main', viewport)}
{layout.showControls && controls.top !== 'none' && this.region('top', controls.top || SequenceView)}
@@ -140,11 +159,69 @@ 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} />
</div>
</div>;
}
}
function dropFiles(ev: React.DragEvent<HTMLDivElement>, plugin: PluginUIContext, showDragOverlay: BehaviorSubject<boolean>) {
ev.preventDefault();
ev.stopPropagation();
showDragOverlay.next(false);
const files: File[] = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file') continue;
const file = ev.dataTransfer.items[i].getAsFile();
if (file) files.push(file);
}
} else {
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[0];
if (file) files.push(file);
}
}
const sessions = files.filter(f => {
const fn = f.name.toLowerCase();
return fn.endsWith('.molx') || fn.endsWith('.molj');
});
if (sessions.length > 0) {
PluginCommands.State.Snapshots.OpenFile(plugin, { file: sessions[0] });
} else {
plugin.runTask(plugin.state.data.applyAction(OpenFiles, {
files: files.map(f => Asset.File(f)),
format: { name: 'auto', params: {} },
visuals: true
}));
}
}
function DragOverlay({ plugin, showDragOverlay }: { plugin: PluginUIContext, showDragOverlay: BehaviorSubject<boolean> }) {
const show = useBehavior(showDragOverlay);
const preventDrag = (e: React.DragEvent) => {
e.dataTransfer.dropEffect = 'copy';
e.preventDefault();
e.stopPropagation();
};
return <div
className='msp-drag-drop-overlay'
style={{ display: show ? 'flex' : 'none' }}
onDragEnter={preventDrag}
onDragOver={preventDrag}
onDragLeave={() => showDragOverlay.next(false)}
onDrop={e => dropFiles(e, plugin, showDragOverlay)}
>
Upload File(s)
</div>;
}
export class ControlsWrapper extends PluginUIComponent {
render() {
const StructureTools = this.plugin.spec.components?.structureTools || DefaultStructureTools;

View File

@@ -529,6 +529,18 @@
}
}
.msp-control-group-presets-wrapper {
position: absolute;
right: 0;
top: 0;
.msp-control-group-header {
background: transparent;
}
button {
background: transparent !important;
}
}
.msp-parameter-matrix {
input {
flex: 1 1 auto;
@@ -614,4 +626,19 @@
.msp-list-unstyled {
padding-left: 0;
list-style: none;
}
.msp-drag-drop-overlay {
border: 12px dashed $font-color;
background: rgba(0, 0, 0, 0.36);
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
font-size: 48px;
font-weight: bold;
}

View File

@@ -20,12 +20,17 @@ export class PluginConfigItem<T = any> {
function item<T>(key: string, defaultValue?: T) { return new PluginConfigItem(key, defaultValue); }
function preferWebGl1() {
export function preferWebGl1() {
if (typeof navigator === 'undefined' || typeof window === 'undefined') return false;
// WebGL2 isn't working in MacOS 12.0.1 Safari 15.1 (but is working in Safari tech preview)
// WebGL2 isn't working in MacOS 12.0.1 Safari 15.1, 15.2. It is working in Safari 15.4 tech preview, so disabling all versions before that.
// prefer webgl 1 based on the userAgent substring
if (navigator.userAgent.indexOf('Version/15.1 Safari') > 0) {
const unpportedSafariVersions = [
'Version/15.1 Safari',
'Version/15.2 Safari',
'Version/15.3 Safari'
];
if (unpportedSafariVersions.some(v => navigator.userAgent.indexOf(v) > 0)) {
return true;
}

View File

@@ -13,6 +13,7 @@ import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { PolymerBackboneSphereParams, PolymerBackboneSphereVisual } from '../visual/polymer-backbone-sphere';
import { PolymerGapParams, PolymerGapVisual } from '../visual/polymer-gap-cylinder';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const BackboneVisuals = {
'polymer-backbone-cylinder': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerBackboneCylinderParams>) => UnitsRepresentation('Polymer backbone cylinder', ctx, getParams, PolymerBackboneCylinderVisual),
@@ -25,7 +26,8 @@ export const BackboneParams = {
...PolymerBackboneCylinderParams,
...PolymerGapParams,
sizeAspectRatio: PD.Numeric(1, { min: 0.1, max: 3, step: 0.1 }),
visuals: PD.MultiSelect(['polymer-backbone-cylinder', 'polymer-backbone-sphere', 'polymer-gap'], PD.objectToOptions(BackboneVisuals))
visuals: PD.MultiSelect(['polymer-backbone-cylinder', 'polymer-backbone-sphere', 'polymer-gap'], PD.objectToOptions(BackboneVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type BackboneParams = typeof BackboneParams
export function getBackboneParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -15,6 +15,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { getUnitKindsParam } from '../params';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const BallAndStickVisuals = {
'element-sphere': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Element sphere', ctx, getParams, ElementSphereVisual),
@@ -31,7 +32,8 @@ export const BallAndStickParams = {
unitKinds: getUnitKindsParam(['atomic']),
sizeFactor: PD.Numeric(0.15, { min: 0.01, max: 10, step: 0.01 }),
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0.01, max: 3, step: 0.01 }),
visuals: PD.MultiSelect(['element-sphere', 'intra-bond', 'inter-bond'], PD.objectToOptions(BallAndStickVisuals))
visuals: PD.MultiSelect(['element-sphere', 'intra-bond', 'inter-bond'], PD.objectToOptions(BallAndStickVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type BallAndStickParams = typeof BallAndStickParams
export function getBallAndStickParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -4,6 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { BaseGeometry } from '../../../mol-geo/geometry/base';
import { Structure, Model } from '../../../mol-model/structure';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
@@ -25,6 +26,7 @@ export const CarbohydrateParams = {
...CarbohydrateLinkParams,
...CarbohydrateTerminalLinkParams,
visuals: PD.MultiSelect(['carbohydrate-symbol', 'carbohydrate-link', 'carbohydrate-terminal-link'], PD.objectToOptions(CarbohydrateVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type CarbohydrateParams = typeof CarbohydrateParams
export function getCarbohydrateParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -18,6 +18,7 @@ import { PolymerTraceParams, PolymerTraceVisual } from '../visual/polymer-trace-
import { SecondaryStructureProvider } from '../../../mol-model-props/computed/secondary-structure';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { HelixOrientationProvider } from '../../../mol-model-props/computed/helix-orientation';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const CartoonVisuals = {
'polymer-trace': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTraceParams>) => UnitsRepresentation('Polymer trace mesh', ctx, getParams, PolymerTraceVisual),
@@ -35,6 +36,7 @@ export const CartoonParams = {
...PolymerDirectionParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
visuals: PD.MultiSelect(['polymer-trace', 'polymer-gap', 'nucleotide-block'], PD.objectToOptions(CartoonVisuals)),
bumpFrequency: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type CartoonParams = typeof CartoonParams

View File

@@ -14,6 +14,7 @@ import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/property
import { IntraUnitBondCylinderParams, IntraUnitBondCylinderVisual } from '../visual/bond-intra-unit-cylinder';
import { InterUnitBondCylinderVisual, InterUnitBondCylinderParams } from '../visual/bond-inter-unit-cylinder';
import { getUnitKindsParam } from '../params';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const EllipsoidVisuals = {
'ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidMeshParams>) => UnitsRepresentation('Ellipsoid Mesh', ctx, getParams, EllipsoidMeshVisual),
@@ -32,6 +33,7 @@ export const EllipsoidParams = {
sizeAspectRatio: PD.Numeric(0.1, { min: 0.01, max: 3, step: 0.01 }),
linkCap: PD.Boolean(true),
visuals: PD.MultiSelect(['ellipsoid-mesh', 'intra-bond', 'inter-bond'], PD.objectToOptions(EllipsoidVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type EllipsoidParams = typeof EllipsoidParams
export function getEllipsoidParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const GaussianSurfaceVisuals = {
'gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface mesh', ctx, getParams, GaussianSurfaceVisual),
@@ -23,6 +24,7 @@ export const GaussianSurfaceParams = {
...GaussianSurfaceMeshParams,
...GaussianWireframeParams,
visuals: PD.MultiSelect(['gaussian-surface-mesh'], PD.objectToOptions(GaussianSurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type GaussianSurfaceParams = typeof GaussianSurfaceParams
export function getGaussianSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { MolecularSurfaceWireframeParams, MolecularSurfaceWireframeVisual } from '../visual/molecular-surface-wireframe';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const MolecularSurfaceVisuals = {
'molecular-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MolecularSurfaceMeshParams>) => UnitsRepresentation('Molecular surface mesh', ctx, getParams, MolecularSurfaceMeshVisual),
@@ -22,6 +23,7 @@ export const MolecularSurfaceParams = {
...MolecularSurfaceMeshParams,
...MolecularSurfaceWireframeParams,
visuals: PD.MultiSelect(['molecular-surface-mesh'], PD.objectToOptions(MolecularSurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type MolecularSurfaceParams = typeof MolecularSurfaceParams
export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -11,6 +11,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { OrientationEllipsoidMeshParams, OrientationEllipsoidMeshVisual } from '../visual/orientation-ellipsoid-mesh';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const OrientationVisuals = {
'orientation-ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, OrientationEllipsoidMeshParams>) => UnitsRepresentation('Orientation ellipsoid mesh', ctx, getParams, OrientationEllipsoidMeshVisual),
@@ -19,6 +20,7 @@ const OrientationVisuals = {
export const OrientationParams = {
...OrientationEllipsoidMeshParams,
visuals: PD.MultiSelect(['orientation-ellipsoid-mesh'], PD.objectToOptions(OrientationVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type OrientationParams = typeof OrientationParams
export function getOrientationParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
import { Structure, Unit } from '../../../mol-model/structure';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const PuttyVisuals = {
'polymer-tube': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTubeParams>) => UnitsRepresentation('Polymer tube mesh', ctx, getParams, PolymerTubeVisual),
@@ -23,6 +24,7 @@ export const PuttyParams = {
...PolymerGapParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
visuals: PD.MultiSelect(['polymer-tube', 'polymer-gap'], PD.objectToOptions(PuttyVisuals)),
bumpFrequency: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type PuttyParams = typeof PuttyParams
export function getPuttyParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -11,6 +11,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { RepresentationParamsGetter, RepresentationContext, Representation } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const SpacefillVisuals = {
'element-sphere': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Sphere mesh', ctx, getParams, ElementSphereVisual),
@@ -18,6 +19,7 @@ const SpacefillVisuals = {
export const SpacefillParams = {
...ElementSphereParams,
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type SpacefillParams = typeof SpacefillParams
export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 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>
@@ -21,11 +21,12 @@ import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper';
export const LabelTextParams = {
...ComplexTextParams,
background: PD.Boolean(true),
background: PD.Boolean(false),
backgroundMargin: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
backgroundColor: PD.Color(ColorNames.black),
backgroundOpacity: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
level: PD.Select('residue', [['chain', 'Chain'], ['residue', 'Residue'], ['element', 'Element']] as const),
borderWidth: PD.Numeric(0.25, { min: 0, max: 0.5, step: 0.01 }),
level: PD.Select('residue', [['chain', 'Chain'], ['residue', 'Residue'], ['element', 'Element']] as const, { isEssential: true }),
chainScale: PD.Numeric(10, { min: 0, max: 20, step: 0.1 }),
residueScale: PD.Numeric(1, { min: 0, max: 20, step: 0.1 }),
elementScale: PD.Numeric(0.5, { min: 0, max: 20, step: 0.1 }),

View File

@@ -28,6 +28,7 @@ import { extractIsosurface } from '../../mol-gl/compute/marching-cubes/isosurfac
import { WebGLContext } from '../../mol-gl/webgl/context';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
import { Texture } from '../../mol-gl/webgl/texture';
import { BaseGeometry } from '../../mol-geo/geometry/base';
export const VolumeIsosurfaceParams = {
isoValue: Volume.IsoValueParam
@@ -260,6 +261,7 @@ export const IsosurfaceParams = {
...IsosurfaceMeshParams,
...IsosurfaceWireframeParams,
visuals: PD.MultiSelect(['solid'], PD.objectToOptions(IsosurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type IsosurfaceParams = typeof IsosurfaceParams
export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: Volume) {

View File

@@ -17,7 +17,8 @@ const DefaultIllustrativeColor = Color(0xEEEEEE);
const Description = `Assigns an illustrative color that gives every chain a unique color with lighter carbons (inspired by David Goodsell's Molecule of the Month style).`;
export const IllustrativeColorThemeParams = {
...ChainIdColorThemeParams
...ChainIdColorThemeParams,
carbonLightness: PD.Numeric(0.8, { min: -6, max: 6, step: 0.1 })
};
export type IllustrativeColorThemeParams = typeof IllustrativeColorThemeParams
export function getIllustrativeColorThemeParams(ctx: ThemeDataContext) {
@@ -29,7 +30,7 @@ export function IllustrativeColorTheme(ctx: ThemeDataContext, props: PD.Values<I
function illustrativeColor(location: Location, typeSymbol: ElementSymbol) {
const baseColor = chainIdColor(location, false);
return typeSymbol === 'C' ? Color.lighten(baseColor, 0.8) : baseColor;
return typeSymbol === 'C' ? Color.lighten(baseColor, props.carbonLightness) : baseColor;
}
function color(location: Location): Color {

View File

@@ -12,6 +12,8 @@ export interface Material {
metalness: number,
/** Normalized to [0, 1] range */
roughness: number
/** Normalized to [0, 1] range */
bumpiness: number
}
export function Material(values?: Partial<Material>) {
@@ -19,29 +21,31 @@ export function Material(values?: Partial<Material>) {
}
export namespace Material {
export const Zero: Material = { metalness: 0, roughness: 0 };
export const Zero: Material = { metalness: 0, roughness: 0, bumpiness: 0 };
export function toArray(material: Material, array: NumberArray, offset: number) {
array[offset] = material.metalness * 255;
array[offset + 1] = material.roughness * 255;
array[offset + 2] = material.bumpiness * 255;
return array;
}
export function toString({ metalness, roughness }: Material) {
return `M ${metalness.toFixed(2)} | R ${roughness.toFixed(2)}`;
export function toString({ metalness, roughness, bumpiness }: Material) {
return `M ${metalness.toFixed(2)} | R ${roughness.toFixed(2)} | B ${bumpiness.toFixed(2)}`;
}
export function getParam(info?: { isExpanded?: boolean, isFlat?: boolean }) {
return PD.Group({
metalness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
roughness: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
bumpiness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
}, {
...info,
presets: [
[{ metalness: 0, roughness: 1 }, 'Matte'],
[{ metalness: 0, roughness: 0.2 }, 'Plastic'],
[{ metalness: 0, roughness: 0.6 }, 'Glossy'],
[{ metalness: 1.0, roughness: 0.6 }, 'Metallic'],
[{ metalness: 0, roughness: 1, bumpiness: 0 }, 'Matte'],
[{ metalness: 0, roughness: 0.2, bumpiness: 0 }, 'Plastic'],
[{ metalness: 0, roughness: 0.6, bumpiness: 0 }, 'Glossy'],
[{ metalness: 1.0, roughness: 0.6, bumpiness: 0 }, 'Metallic'],
]
});
}