Compare commits

...

56 Commits

Author SHA1 Message Date
dsehnal
8a76a3fa64 2.0.4 2021-04-20 11:23:23 +02:00
dsehnal
71bf4e21f5 changelog 2021-04-20 11:21:29 +02:00
dsehnal
e0d36c30d3 Fix measurement labels & interactions 2021-04-20 11:19:15 +02:00
David Sehnal
b53debcfef Merge pull request #163 from sukolsak/fix-zip-progress
Fix incorrect deflate progress
2021-04-14 15:02:15 +02:00
Sukolsak Sakshuwong
d0705ac226 fix deflate progress 2021-04-14 05:07:05 -07:00
Alexander Rose
e01eacb3fe changelog 2021-04-13 23:12:39 -07:00
Alexander Rose
d4102b476b Fix, read SDF multi-line values 2021-04-13 23:07:55 -07:00
dsehnal
83ce17174a changelog 2021-04-13 21:17:28 +02:00
dsehnal
18023d7f26 Merge branch 'master' of https://github.com/molstar/molstar 2021-04-13 21:16:28 +02:00
dsehnal
a8541d5967 Structure.eachAtomicHierarchyElement 2021-04-13 21:15:44 +02:00
David Sehnal
8b21818f2e Merge pull request #159 from sukolsak/obj-export
OBJ Export
2021-04-13 17:06:48 +02:00
Sukolsak Sakshuwong
0b290247dc show partial progress when exporting large meshes 2021-04-11 13:15:50 -07:00
Sukolsak Sakshuwong
fb5010e962 reorder arguments to addMeshWithColors() 2021-04-10 18:12:32 -07:00
Sukolsak Sakshuwong
178789d327 make RenderObjectExporter.add() async 2021-04-10 18:10:42 -07:00
Sukolsak Sakshuwong
4fae526073 wip 2021-04-09 15:59:14 -07:00
dsehnal
05f1d8085a 2.0.3 2021-04-09 17:47:12 +02:00
dsehnal
38bbabd742 fix test 2021-04-09 17:45:51 +02:00
dsehnal
3ab958a93c changelog 2021-04-09 17:43:23 +02:00
dsehnal
f59d589a30 CubeGridFormat 2021-04-09 17:41:13 +02:00
David Sehnal
11f7e54704 Merge pull request #158 from molstar/gradient-themes
Add ColorTheme.palette support
2021-04-09 10:01:59 +02:00
dsehnal
16ebd8266e changelog 2021-04-09 10:01:21 +02:00
dsehnal
7a796a4d3d Merge branch 'master' of https://github.com/molstar/molstar into gradient-themes 2021-04-09 09:59:46 +02:00
Alexander Rose
1cbb915962 started a changelog 2021-04-08 23:14:55 -07:00
Alexander Rose
80486d58c3 async deflate (and zip) 2021-04-08 23:13:30 -07:00
dsehnal
81bc116c4d TextureImage.filter 2021-04-08 09:49:35 +02:00
dsehnal
4249064dd1 Add ColorTheme.palette support
- add example to basic-wrapper that uses it
2021-04-07 15:55:54 +02:00
dsehnal
028c02f50d 2.0.2 2021-03-29 12:48:26 +02:00
dsehnal
76e97d7b59 fix VolumeFromDensityServerCif label 2021-03-29 12:46:10 +02:00
dsehnal
ad1181a75b interpolate ModelFromTrajectory transform 2021-03-29 12:03:13 +02:00
Alexander Rose
5d683462fb add common non-standard amino-acids 2021-03-28 15:38:15 -07:00
Alexander Rose
42422bb0ea add canvas3d.getRenderObjects 2021-03-28 15:36:40 -07:00
dsehnal
861e5c3e97 SDF data item test 2021-03-24 16:02:53 +01:00
dsehnal
614cffda96 2.0.1 2021-03-23 15:44:24 +01:00
dsehnal
2e0379d202 npm ignore 2021-03-23 15:42:52 +01:00
dsehnal
b5cfdcd2a3 2.0.0 2021-03-23 15:31:18 +01:00
dsehnal
c00de6fde0 Merge branch 'master' of https://github.com/molstar/molstar 2021-03-23 11:16:26 +01:00
Alexander Rose
da3a8e56f3 handle negative isovalues in gpu mc 2021-03-22 22:06:49 -07:00
dsehnal
103d6fe775 alpha orbitals tryUseGpu param 2021-03-22 20:18:48 +01:00
dsehnal
5df55e6bf7 SDF delimiter bugfix + multi-molecule SDF support in mol-plugin 2021-03-22 17:21:47 +01:00
dsehnal
3b285086d4 rename files called "macro" due to Jest not being able to process them 2021-03-22 16:53:48 +01:00
dsehnal
91793bc3cc 2.0.0-dev.13 2021-03-22 13:29:42 +01:00
dsehnal
fa3828e820 add model-server-query transform support 2021-03-22 12:51:03 +01:00
Alexander Rose
31ba8212da 2.0.0-dev.12 2021-03-21 16:47:53 -07:00
Alexander Rose
fe27d8e134 Merge pull request #150 from molstar/stubs2
basic support for bond stubs
2021-03-21 16:40:03 -07:00
Alexander Rose
83dcdfdc4b Merge commit '2faa821c50a6dfce700eb8072a61d01d937c18e5' into stubs2 2021-03-21 16:36:33 -07:00
Alexander Rose
f9aaabc1f7 fix interactions bounding sphere 2021-03-21 16:29:56 -07:00
Alexander Rose
034370b44c add includeParent support to interactions 2021-03-21 16:25:03 -07:00
Alexander Rose
b87666df3e don't pad empty bounding spheres 2021-03-21 16:24:25 -07:00
Alexander Rose
c98c3228fe fix structure.asParent 2021-03-21 16:23:57 -07:00
Alexander Rose
9419980dfc make structure state private (like before) 2021-03-21 12:39:21 -07:00
Alexander Rose
42d60420e5 added Structure.asParent
- refactored structure state handling
- removed Structure.WithChild
2021-03-21 12:10:24 -07:00
dsehnal
5b1df333a7 tsconfig jsx param 2021-03-21 16:18:01 +01:00
Alexander Rose
0bb376706d fix ellipsoid repr and support includeParent
- switch off adjustCylinderLength
- handle structure with child
2021-03-20 23:58:48 -07:00
Alexander Rose
eca7da2c72 add adjustCylinderLength param
- so it can be switched off
2021-03-20 23:54:50 -07:00
Alexander Rose
b0bdb3ddb6 tweak param help 2021-03-20 23:52:23 -07:00
Alexander Rose
3180d7c305 basic support for bond stubs
- line and ball & stick repr
- stubs support in link visual helper
- getData and mustRecreate methods for structure repr provider
- Structure.WithChild helper (needs Proxy support)
2021-03-20 18:05:58 -07:00
101 changed files with 1919 additions and 591 deletions

View File

@@ -1 +1 @@
tsconfig.commonjs.buildinfo
tsconfig.commonjs.tsbuildinfo

39
CHANGELOG.md Normal file
View File

@@ -0,0 +1,39 @@
# Change Log
All notable changes to this project will be documented in this file, following the suggestions of [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/) for its most widely used - and defacto - public interfaces.
Note that since we don't clearly distinguish between a public and private interfaces there will be changes in non-major versions that are potentially breaking. If we make breaking changes to less used interfaces we will highlight it in here.
## [v2.0.4] - 2021-04-20
- [WIP] Mesh export extension
- ``Structure.eachAtomicHierarchyElement`` (#161)
- Fixed reading multi-line values in SDF format
- Fixed Measurements UI labels (#166)
## [v2.0.3] - 2021-04-09
### Added
- Support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
### Changed
- [Breaking] The `zip` function is now asynchronous and expects a `RuntimeContext`. Also added `Zip()` returning a `Task`.
- [Breaking] Add ``CubeGridFormat`` in ``alpha-orbitals`` extension.
## [v2.0.2] - 2021-03-29
### Added
- `Canvas3D.getRenderObjects`.
- [WIP] Animate state interpolating, including model trajectories
### Changed
- Recognise MSE, SEP, TPO, PTR and PCA as non-standard amino-acids.
### Fixed
- VolumeFromDensityServerCif transform label
## [v2.0.1] - 2021-03-23
### Fixed
- Exclude tsconfig.commonjs.tsbuildinfo from npm bundle
## [v2.0.0] - 2021-03-23
Too many changes to list as this is the start of the changelog... Notably, default exports are now forbidden.

View File

@@ -27,3 +27,4 @@
* DNA (5D3G)
* Multiple models with different sets of ligands or missing ligands (1J6T, 1VRC, 2ICY, 1O2F)
* Long linear sugar chain (4HG6)
* Anisotropic B-factors/Ellipsoids (1EJG)

4
package-lock.json generated
View File

@@ -1,11 +1,11 @@
{
"name": "molstar",
"version": "2.0.0-dev.11",
"version": "2.0.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "2.0.0-dev.11",
"version": "2.0.4",
"license": "MIT",
"dependencies": {
"@types/argparse": "^1.0.38",

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "2.0.0-dev.11",
"version": "2.0.4",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -14,6 +14,7 @@
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"test": "npm run lint && jest",
"jest": "jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",

View File

@@ -4,24 +4,23 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
import { LociLabels } from '../../mol-plugin-ui/controls';
import { Toasts } from '../../mol-plugin-ui/toast';
import { Button } from '../../mol-plugin-ui/controls/common';
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
import { StateObjectRef } from '../../mol-state';
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { InteractionsRepresentationProvider } from '../../mol-model-props/computed/representations/interactions';
import { InteractionTypeColorThemeProvider } from '../../mol-model-props/computed/themes/interaction-type';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { presetStaticComponent, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
import { Color } from '../../mol-util/color';
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import { LociLabels } from '../../mol-plugin-ui/controls';
import { Button } from '../../mol-plugin-ui/controls/common';
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
import { Toasts } from '../../mol-plugin-ui/toast';
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateObjectRef } from '../../mol-state';
import { Color } from '../../mol-util/color';
function shinyStyle(plugin: PluginContext) {
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {

View File

@@ -10,6 +10,7 @@ import { CellPack } from '../../extensions/cellpack';
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
import { Mp4Export } from '../../extensions/mp4-export';
import { GeometryExport } from '../../extensions/geo-export';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
@@ -55,7 +56,8 @@ const Extensions = {
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
'g3d': PluginSpec.Behavior(G3DFormat),
'mp4-export': PluginSpec.Behavior(Mp4Export)
'mp4-export': PluginSpec.Behavior(Mp4Export),
'geo-export': PluginSpec.Behavior(GeometryExport)
};
const DefaultViewerOptions = {

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AlphaOrbitalsExample } from '.';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';

View File

@@ -174,7 +174,8 @@ export class AlphaOrbitalsExample {
kind,
relativeIsovalue: this.state.value.isoValue,
pickable: false,
xrayShaded: true
xrayShaded: true,
tryUseGpu: false
};
}

View File

@@ -5,7 +5,6 @@
*/
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import * as React from 'react';
export class CustomToastMessage extends PluginUIComponent {
render() {

View File

@@ -0,0 +1,51 @@
import { isPositionLocation } from '../../mol-geo/util/location-iterator';
import { Vec3 } from '../../mol-math/linear-algebra';
import { ColorTheme } from '../../mol-theme/color';
import { ThemeDataContext } from '../../mol-theme/theme';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
export function CustomColorTheme(
ctx: ThemeDataContext,
props: PD.Values<{}>
): ColorTheme<{}> {
const { radius, center } = ctx.structure?.boundary.sphere!;
const radiusSq = Math.max(radius * radius, 0.001);
const scale = ColorTheme.PaletteScale;
return {
factory: CustomColorTheme,
granularity: 'vertex',
color: location => {
if (!isPositionLocation(location)) return ColorNames.black;
const dist = Vec3.squaredDistance(location.position, center);
const t = Math.min(dist / radiusSq, 1);
return ((t * scale) | 0) as Color;
},
palette: {
filter: 'nearest',
colors: [
ColorNames.red,
ColorNames.pink,
ColorNames.violet,
ColorNames.orange,
ColorNames.yellow,
ColorNames.green,
ColorNames.blue
]
},
props: props,
description: '',
};
}
export const CustomColorThemeProvider: ColorTheme.Provider<{}, 'basic-wrapper-custom-color-theme'> = {
name: 'basic-wrapper-custom-color-theme',
label: 'Custom Color Theme',
category: ColorTheme.Category.Misc,
factory: CustomColorTheme,
getParams: () => ({}),
defaultValues: { },
isApplicable: (ctx: ThemeDataContext) => true,
};

View File

@@ -97,6 +97,7 @@
addHeader('Misc');
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
addControl('Apply Custom Theme', () => BasicMolStarWrapper.coloring.applyCustomTheme());
addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
addHeader('Interactivity');

View File

@@ -18,6 +18,7 @@ import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { StripedResidues } from './coloring';
import { CustomToastMessage } from './controls';
import { CustomColorThemeProvider } from './custom-theme';
import './index.html';
import { buildStaticSuperposition, dynamicSuperpositionTest, StaticSuperpositionTestData } from './superposition';
require('mol-plugin-ui/skin/light.scss');
@@ -42,6 +43,7 @@ class BasicWrapper {
});
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
this.plugin.representation.structure.themes.colorThemeRegistry.add(CustomColorThemeProvider);
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
@@ -103,6 +105,13 @@ class BasicWrapper {
}
});
},
applyCustomTheme: async () => {
this.plugin.dataTransaction(async () => {
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: CustomColorThemeProvider.name as any });
}
});
},
applyDefault: async () => {
this.plugin.dataTransaction(async () => {
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PluginUIContext } from '../../../mol-plugin-ui/context';
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';

View File

@@ -9,6 +9,7 @@ import { Grid } from '../../mol-model/volume';
import { SphericalBasisOrder } from './spherical-functions';
import { Box3D, RegularGrid3d } from '../../mol-math/geometry';
import { arrayMin, arrayMax, arrayRms, arrayMean } from '../../mol-util/array';
import { ModelFormat } from '../../mol-model-formats/format';
// Note: generally contracted gaussians are currently not supported.
export interface SphericalElectronShell {
@@ -59,6 +60,17 @@ export interface CubeGrid {
isovalues?: { negative?: number; positive?: number };
}
export type CubeGridFormat = ModelFormat<CubeGrid>;
// eslint-disable-next-line
export function CubeGridFormat(grid: CubeGrid): CubeGridFormat {
return { name: 'custom grid', kind: 'cube-grid', data: grid };
}
export function isCubeGridData(f: ModelFormat): f is CubeGridFormat {
return f.kind === 'cube-grid';
}
export function initCubeGrid(params: CubeGridComputationParams): CubeGridInfo {
const geometry = params.basis.atoms.map(a => a.center);
const { gridSpacing: spacing, boxExpand: expand } = params;

View File

@@ -17,7 +17,7 @@ import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers
import { StateTransformer } from '../../mol-state';
import { Theme } from '../../mol-theme/theme';
import { VolumeRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
import { AlphaOrbital, Basis, CubeGrid } from './data-model';
import { AlphaOrbital, Basis, CubeGrid, CubeGridFormat, isCubeGridData } from './data-model';
import { createSphericalCollocationDensityGrid } from './density';
import { Tensor } from '../../mol-math/linear-algebra';
@@ -114,7 +114,7 @@ export const CreateOrbitalVolume = PluginStateTransform.BuiltIn({
}, a.data.orbitals[params.index], plugin.canvas3d?.webgl).runInContext(ctx);
const volume: Volume = {
grid: data.grid,
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
sourceData: CubeGridFormat(data),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
@@ -146,7 +146,7 @@ export const CreateOrbitalDensityVolume = PluginStateTransform.BuiltIn({
}, a.data.orbitals, plugin.canvas3d?.webgl).runInContext(ctx);
const volume: Volume = {
grid: data.grid,
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
sourceData: CubeGridFormat(data),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
@@ -172,7 +172,8 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
color: PD.Color(ColorNames.blue),
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
xrayShaded: PD.Boolean(false),
pickable: PD.Boolean(true)
pickable: PD.Boolean(true),
tryUseGpu: PD.Boolean(true)
}
})({
canAutoUpdate() {
@@ -190,7 +191,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, params));
await repr.createOrUpdate(props, a.data).runInContext(ctx);
repr.setState({ pickable: srcParams.pickable });
return new PluginStateObject.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
return new PluginStateObject.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
});
},
update({ a, b, newParams: srcParams }, plugin: PluginContext) {
@@ -200,6 +201,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams.type.params };
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.sourceData = a.data;
b.data.repr.setState({ pickable: srcParams.pickable });
b.description = VolumeRepresentation3DHelpers.getDescription(props);
return StateTransformer.UpdateResult.Updated;
@@ -208,9 +210,9 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
});
function volumeParams(plugin: PluginContext, volume: PluginStateObject.Volume.Data, params: StateTransformer.Params<typeof CreateOrbitalRepresentation3D>) {
if (volume.data.sourceData.kind !== 'alpha-orbitals') throw new Error('Invalid data source kind.');
if (!isCubeGridData(volume.data.sourceData)) throw new Error('Invalid data source kind.');
const { isovalues } = volume.data.sourceData.data as CubeGrid;
const { isovalues } = volume.data.sourceData.data;
if (!isovalues) throw new Error('Isovalues are not computed.');
const value = isovalues[params.kind];
@@ -229,7 +231,7 @@ function volumeParams(plugin: PluginContext, volume: PluginStateObject.Volume.Da
colorParams: { value: params.color }
} : {
type: 'isosurface',
typeParams: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, alpha: params.alpha, xrayShaded: params.xrayShaded },
typeParams: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, alpha: params.alpha, xrayShaded: params.xrayShaded, tryUseGpu: params.tryUseGpu },
color: 'uniform',
colorParams: { value: params.color }
});

View File

@@ -121,7 +121,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
const repr = MembraneOrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => MembraneOrientationParams);
await repr.createOrUpdate(params, a.data).runInContext(ctx);
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: 'Membrane Orientation' });
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: 'Membrane Orientation' });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
@@ -129,6 +129,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
const props = { ...b.data.repr.props, ...newParams };
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.sourceData = a.data;
return StateTransformer.UpdateResult.Updated;
});
},

View File

@@ -414,7 +414,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
const structure = new Structure(units);
const structure = Structure.create(units);
for( let i = 0, il = structure.models.length; i < il; ++i) {
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { PluginComponent } from '../../mol-plugin-state/component';
import { PluginContext } from '../../mol-plugin/context';
import { Task } from '../../mol-task';
import { ObjExporter } from './export';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateSelection } from '../../mol-state';
import { SetUtils } from '../../mol-util/set';
import { zip } from '../../mol-util/zip/zip';
export class GeometryControls extends PluginComponent {
getFilename() {
const models = this.plugin.state.data.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Model)).map(s => s.obj!.data);
const uniqueIds = new Set<string>();
models.forEach(m => uniqueIds.add(m.entryId.toUpperCase()));
const idString = SetUtils.toArray(uniqueIds).join('-');
return `${idString || 'molstar-model'}`;
}
exportObj() {
const task = Task.create('Export OBJ', async ctx => {
try {
const renderObjects = this.plugin.canvas3d?.getRenderObjects()!;
const filename = this.getFilename();
const objExporter = new ObjExporter(filename);
for (let i = 0, il = renderObjects.length; i < il; ++i) {
await ctx.update({ message: `Exporting object ${i}/${il}` });
await objExporter.add(renderObjects[i], ctx);
}
const { obj, mtl } = objExporter.getData();
const asciiWrite = (data: Uint8Array, str: string) => {
for (let i = 0, il = str.length; i < il; ++i) {
data[i] = str.charCodeAt(i);
}
};
const objData = new Uint8Array(obj.length);
asciiWrite(objData, obj);
const mtlData = new Uint8Array(mtl.length);
asciiWrite(mtlData, mtl);
const zipDataObj = {
[filename + '.obj']: objData,
[filename + '.mtl']: mtlData
};
const zipData = await zip(ctx, zipDataObj);
return {
zipData,
filename: filename + '.zip'
};
} catch (e) {
this.plugin.log.error('' + e);
throw e;
}
});
return this.plugin.runTask(task, { useOverlay: true });
}
constructor(private plugin: PluginContext) {
super();
}
}

View File

@@ -0,0 +1,321 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { GraphicsRenderObject } from '../../mol-gl/render-object';
import { MeshValues } from '../../mol-gl/renderable/mesh';
import { LinesValues } from '../../mol-gl/renderable/lines';
import { PointsValues } from '../../mol-gl/renderable/points';
import { SpheresValues } from '../../mol-gl/renderable/spheres';
import { CylindersValues } from '../../mol-gl/renderable/cylinders';
import { BaseValues, SizeValues } from '../../mol-gl/renderable/schema';
import { TextureImage } from '../../mol-gl/renderable/util';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
import { RuntimeContext } from '../../mol-task';
import { StringBuilder } from '../../mol-util';
import { Color } from '../../mol-util/color/color';
import { decodeFloatRGB } from '../../mol-util/float-packing';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
const v3transformMat4 = Vec3.transformMat4;
const v3transformMat3 = Vec3.transformMat3;
const mat3directionTransform = Mat3.directionTransform;
type RenderObjectExportData = {
[k: string]: string | Uint8Array | undefined
}
interface RenderObjectExporter<D extends RenderObjectExportData> {
add(renderObject: GraphicsRenderObject, ctx: RuntimeContext): Promise<void> | undefined
getData(): D
}
// http://paulbourke.net/dataformats/obj/
// http://paulbourke.net/dataformats/mtl/
export type ObjData = {
obj: string
mtl: string
}
export class ObjExporter implements RenderObjectExporter<ObjData> {
private obj = StringBuilder.create();
private mtl = StringBuilder.create();
private vertexOffset = 0;
private currentColor: Color | undefined;
private currentAlpha: number | undefined;
private materialSet = new Set<string>();
private static getSizeFromTexture(tSize: TextureImage<Uint8Array>, i: number): number {
const r = tSize.array[i * 3];
const g = tSize.array[i * 3 + 1];
const b = tSize.array[i * 3 + 2];
return decodeFloatRGB(r, g, b);
}
private static getSize(values: BaseValues & SizeValues, instanceIndex: number, group: number): number {
const tSize = values.tSize.ref.value;
let size = 0;
switch (values.dSizeType.ref.value) {
case 'uniform':
size = values.uSize.ref.value;
break;
case 'instance':
size = ObjExporter.getSizeFromTexture(tSize, instanceIndex) / 100;
break;
case 'group':
size = ObjExporter.getSizeFromTexture(tSize, group) / 100;
break;
case 'groupInstance':
const groupCount = values.uGroupCount.ref.value;
size = ObjExporter.getSizeFromTexture(tSize, instanceIndex * groupCount + group) / 100;
break;
}
return size * values.uSizeFactor.ref.value;
}
private updateMaterial(color: Color, alpha: number) {
if (this.currentColor === color && this.currentAlpha === alpha) return;
this.currentColor = color;
this.currentAlpha = alpha;
const material = Color.toHexString(color) + alpha;
StringBuilder.writeSafe(this.obj, `usemtl ${material}`);
StringBuilder.newline(this.obj);
if (!this.materialSet.has(material)) {
this.materialSet.add(material);
const [r, g, b] = Color.toRgbNormalized(color);
const mtl = this.mtl;
StringBuilder.writeSafe(mtl, `newmtl ${material}\n`);
StringBuilder.writeSafe(mtl, 'illum 2\n'); // illumination model
StringBuilder.writeSafe(mtl, 'Ns 163\n'); // specular exponent
StringBuilder.writeSafe(mtl, 'Ni 0.001\n'); // optical density a.k.a. index of refraction
StringBuilder.writeSafe(mtl, 'Ka 0 0 0\n'); // ambient reflectivity
StringBuilder.writeSafe(mtl, 'Kd '); // diffuse reflectivity
StringBuilder.writeFloat(mtl, r, 1000);
StringBuilder.whitespace1(mtl);
StringBuilder.writeFloat(mtl, g, 1000);
StringBuilder.whitespace1(mtl);
StringBuilder.writeFloat(mtl, b, 1000);
StringBuilder.newline(mtl);
StringBuilder.writeSafe(mtl, 'Ks 0.25 0.25 0.25\n'); // specular reflectivity
StringBuilder.writeSafe(mtl, 'd '); // dissolve
StringBuilder.writeFloat(mtl, alpha, 1000);
StringBuilder.newline(mtl);
}
}
private async addMeshWithColors(vertices: Float32Array, normals: Float32Array, indices: Uint32Array, groups: Float32Array, vertexCount: number, drawCount: number, values: BaseValues, instanceIndex: number, ctx: RuntimeContext) {
const obj = this.obj;
const t = Mat4();
const n = Mat3();
const tmpV = Vec3();
const colorType = values.dColorType.ref.value;
const tColor = values.tColor.ref.value.array;
const uAlpha = values.uAlpha.ref.value;
const aTransform = values.aTransform.ref.value;
Mat4.fromArray(t, aTransform, instanceIndex * 16);
mat3directionTransform(n, t);
const currentProgress = (vertexCount * 2 + drawCount) * instanceIndex;
await ctx.update({ isIndeterminate: false, current: currentProgress, max: (vertexCount * 2 + drawCount) * values.uInstanceCount.ref.value });
// position
for (let i = 0; i < vertexCount; ++i) {
if (i % 1000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + i });
v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * 3), t);
StringBuilder.writeSafe(obj, 'v ');
StringBuilder.writeFloat(obj, tmpV[0], 1000);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[1], 1000);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[2], 1000);
StringBuilder.newline(obj);
}
// normal
for (let i = 0; i < vertexCount; ++i) {
if (i % 1000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + vertexCount + i });
v3transformMat3(tmpV, v3fromArray(tmpV, normals, i * 3), n);
StringBuilder.writeSafe(obj, 'vn ');
StringBuilder.writeFloat(obj, tmpV[0], 100);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[1], 100);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[2], 100);
StringBuilder.newline(obj);
}
// face
for (let i = 0; i < drawCount; i += 3) {
if (i % 3000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + vertexCount * 2 + i });
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(values.uColor.ref.value, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group':
color = Color.fromArray(tColor, groups[indices[i]] * 3);
break;
case 'groupInstance':
const groupCount = values.uGroupCount.ref.value;
const group = groups[indices[i]];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
case 'vertex':
color = Color.fromArray(tColor, i * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * drawCount + i) * 3);
break;
default: throw new Error('Unsupported color type.');
}
this.updateMaterial(color, uAlpha);
const v1 = this.vertexOffset + indices[i] + 1;
const v2 = this.vertexOffset + indices[i + 1] + 1;
const v3 = this.vertexOffset + indices[i + 2] + 1;
StringBuilder.writeSafe(obj, 'f ');
StringBuilder.writeInteger(obj, v1);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeIntegerAndSpace(obj, v1);
StringBuilder.writeInteger(obj, v2);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeIntegerAndSpace(obj, v2);
StringBuilder.writeInteger(obj, v3);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeInteger(obj, v3);
StringBuilder.newline(obj);
}
this.vertexOffset += vertexCount;
}
private async addMesh(values: MeshValues, ctx: RuntimeContext) {
const aPosition = values.aPosition.ref.value;
const aNormal = values.aNormal.ref.value;
const elements = values.elements.ref.value;
const aGroup = values.aGroup.ref.value;
const instanceCount = values.instanceCount.ref.value;
const vertexCount = values.uVertexCount.ref.value;
const drawCount = values.drawCount.ref.value;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
await this.addMeshWithColors(aPosition, aNormal, elements, aGroup, vertexCount, drawCount, values, instanceIndex, ctx);
}
}
private async addLines(values: LinesValues, ctx: RuntimeContext) {
// TODO
}
private async addPoints(values: PointsValues, ctx: RuntimeContext) {
// TODO
}
private async addSpheres(values: SpheresValues, ctx: RuntimeContext) {
const center = Vec3();
const aPosition = values.aPosition.ref.value;
const aGroup = values.aGroup.ref.value;
const instanceCount = values.instanceCount.ref.value;
const vertexCount = values.uVertexCount.ref.value;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
const state = MeshBuilder.createState(512, 256);
for (let i = 0; i < vertexCount; i += 4) {
v3fromArray(center, aPosition, i * 3);
const group = aGroup[i];
const radius = ObjExporter.getSize(values, instanceIndex, group);
state.currentGroup = group;
addSphere(state, center, radius, 2);
}
const mesh = MeshBuilder.getMesh(state);
const vertices = mesh.vertexBuffer.ref.value;
const normals = mesh.normalBuffer.ref.value;
const indices = mesh.indexBuffer.ref.value;
const groups = mesh.groupBuffer.ref.value;
await this.addMeshWithColors(vertices, normals, indices, groups, vertices.length / 3, indices.length, values, instanceIndex, ctx);
}
}
private async addCylinders(values: CylindersValues, ctx: RuntimeContext) {
const start = Vec3();
const end = Vec3();
const aStart = values.aStart.ref.value;
const aEnd = values.aEnd.ref.value;
const aScale = values.aScale.ref.value;
const aCap = values.aCap.ref.value;
const aGroup = values.aGroup.ref.value;
const instanceCount = values.instanceCount.ref.value;
const vertexCount = values.uVertexCount.ref.value;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
const state = MeshBuilder.createState(512, 256);
for (let i = 0; i < vertexCount; i += 6) {
v3fromArray(start, aStart, i * 3);
v3fromArray(end, aEnd, i * 3);
const group = aGroup[i];
const radius = ObjExporter.getSize(values, instanceIndex, group) * aScale[i];
const cap = aCap[i];
const topCap = cap === 1 || cap === 3;
const bottomCap = cap >= 2;
const cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap, bottomCap, radialSegments: 32 };
state.currentGroup = aGroup[i];
addCylinder(state, start, end, 1, cylinderProps);
}
const mesh = MeshBuilder.getMesh(state);
const vertices = mesh.vertexBuffer.ref.value;
const normals = mesh.normalBuffer.ref.value;
const indices = mesh.indexBuffer.ref.value;
const groups = mesh.groupBuffer.ref.value;
await this.addMeshWithColors(vertices, normals, indices, groups, vertices.length / 3, indices.length, values, instanceIndex, ctx);
}
}
add(renderObject: GraphicsRenderObject, ctx: RuntimeContext) {
if (!renderObject.state.visible) return;
switch (renderObject.type) {
case 'mesh':
return this.addMesh(renderObject.values as MeshValues, ctx);
case 'lines':
return this.addLines(renderObject.values as LinesValues, ctx);
case 'points':
return this.addPoints(renderObject.values as PointsValues, ctx);
case 'spheres':
return this.addSpheres(renderObject.values as SpheresValues, ctx);
case 'cylinders':
return this.addCylinders(renderObject.values as CylindersValues, ctx);
}
}
getData() {
return {
obj: StringBuilder.getString(this.obj),
mtl: StringBuilder.getString(this.mtl)
};
}
constructor(filename: string) {
StringBuilder.writeSafe(this.obj, `mtllib ${filename}.mtl\n`);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { GeometryExporterUI } from './ui';
export const GeometryExport = PluginBehavior.create<{ }>({
name: 'extension-geo-export',
category: 'misc',
display: {
name: 'Geometry Export'
},
ctor: class extends PluginBehavior.Handler<{ }> {
register(): void {
this.ctx.customStructureControls.set('geo-export', GeometryExporterUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customStructureControls.delete('geo-export');
}
},
params: () => ({ })
});

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { GetAppSvg, CubeSendSvg } from '../../mol-plugin-ui/controls/icons';
import { download } from '../../mol-util/download';
import { GeometryControls } from './controls';
interface State {
busy?: boolean
}
export class GeometryExporterUI extends CollapsableControls<{}, State> {
private _controls: GeometryControls | undefined;
get controls() {
return this._controls || (this._controls = new GeometryControls(this.plugin));
}
protected defaultState(): State & CollapsableState {
return {
header: 'Export Geometries',
isCollapsed: true,
brand: { accent: 'cyan', svg: CubeSendSvg }
};
}
protected renderControls(): JSX.Element {
return <>
<Button icon={GetAppSvg}
onClick={this.saveObj} style={{ marginTop: 1 }}
disabled={this.state.busy || !this.plugin.canvas3d?.reprCount.value}>
Save OBJ + MTL
</Button>
</>;
}
componentDidMount() {
this.subscribe(this.plugin.canvas3d!.reprCount, () => {
if (!this.state.isCollapsed) this.forceUpdate();
});
}
componentWillUnmount() {
this._controls?.dispose();
this._controls = void 0;
}
saveObj = async () => {
try {
this.setState({ busy: true });
const data = await this.controls.exportObj();
this.setState({ busy: false });
download(new Blob([data.zipData]), data.filename);
} catch {
this.setState({ busy: false });
}
}
}

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import React from 'react';
import { merge } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';

View File

@@ -124,7 +124,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AssemblySymmetryParams);
await repr.createOrUpdate(params, a.data).runInContext(ctx);
const { type, kind, symbol } = assemblySymmetry;
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: kind, description: `${type} (${symbol})` });
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: kind, description: `${type} (${symbol})` });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
@@ -138,6 +138,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
}
const props = { ...b.data.repr.props, ...newParams };
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.sourceData = a.data;
const { type, kind, symbol } = assemblySymmetry;
b.label = kind;
b.description = `${type} (${symbol})`;

View File

@@ -4,7 +4,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { CollapsableState, CollapsableControls } from '../../../mol-plugin-ui/base';
import { ApplyActionControl } from '../../../mol-plugin-ui/state/apply-action';
import { InitAssemblySymmetry3D, AssemblySymmetry3D, AssemblySymmetryPreset, tryCreateAssemblySymmetry } from './behavior';

View File

@@ -234,6 +234,7 @@ interface Canvas3D {
readonly boundingSphere: Readonly<Sphere3D>
setProps(props: PartialCanvas3DProps | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void), doNotRequestDraw?: boolean /* = false */): void
getImagePass(props: Partial<ImageProps>): ImagePass
getRenderObjects(): GraphicsRenderObject[]
/** Returns a copy of the current Canvas3D instance props */
readonly props: Readonly<Canvas3DProps>
@@ -769,6 +770,11 @@ namespace Canvas3D {
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
},
getRenderObjects(): GraphicsRenderObject[] {
const renderObjects: GraphicsRenderObject[] = [];
scene.forEach((_, ro) => renderObjects.push(ro));
return renderObjects;
},
get props() {
return getProps();

View File

@@ -18,11 +18,24 @@ export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 've
export type ColorData = {
uColor: ValueCell<Vec3>,
tColor: ValueCell<TextureImage<Uint8Array>>,
tPalette: ValueCell<TextureImage<Uint8Array>>,
uColorTexDim: ValueCell<Vec2>,
dColorType: ValueCell<string>,
dUsePalette: ValueCell<boolean>,
}
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
const data = _createColors(locationIt, positionIt, colorTheme, colorData);
if (colorTheme.palette) {
ValueCell.updateIfChanged(data.dUsePalette, true);
updatePaletteTexture(colorTheme.palette, data.tPalette);
} else {
ValueCell.updateIfChanged(data.dUsePalette, false);
}
return data;
}
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
@@ -42,18 +55,20 @@ export function createValueColor(value: Color, colorData?: ColorData): ColorData
return {
uColor: ValueCell.create(Color.toVec3Normalized(Vec3(), value)),
tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
dColorType: ValueCell.create('uniform'),
dUsePalette: ValueCell.create(false),
};
}
}
/** Creates color uniform */
export function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
return createValueColor(color(NullLocation, false), colorData);
}
export function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
if (colorData) {
ValueCell.update(colorData.tColor, colors);
ValueCell.update(colorData.uColorTexDim, Vec2.create(colors.width, colors.height));
@@ -63,14 +78,16 @@ export function createTextureColor(colors: TextureImage<Uint8Array>, type: Color
return {
uColor: ValueCell.create(Vec3()),
tColor: ValueCell.create(colors),
tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
uColorTexDim: ValueCell.create(Vec2.create(colors.width, colors.height)),
dColorType: ValueCell.create(type),
dUsePalette: ValueCell.create(false),
};
}
}
/** Creates color texture with color for each instance */
export function createInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
const { instanceCount } = locationIt;
const colors = createTextureImage(Math.max(1, instanceCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
locationIt.reset();
@@ -83,7 +100,7 @@ export function createInstanceColor(locationIt: LocationIterator, color: Locatio
}
/** Creates color texture with color for each group (i.e. shared across instances) */
export function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
const { groupCount } = locationIt;
const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
locationIt.reset();
@@ -95,7 +112,7 @@ export function createGroupColor(locationIt: LocationIterator, color: LocationCo
}
/** Creates color texture with color for each group in each instance */
export function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
const { groupCount, instanceCount } = locationIt;
const count = instanceCount * groupCount;
const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
@@ -108,7 +125,7 @@ export function createGroupInstanceColor(locationIt: LocationIterator, color: Lo
}
/** Creates color texture with color for each vertex (i.e. shared across instances) */
export function createVertexColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createVertexColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
const { groupCount, stride } = locationIt;
const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
locationIt.reset();
@@ -124,7 +141,7 @@ export function createVertexColor(locationIt: LocationIterator, color: LocationC
}
/** Creates color texture with color for each vertex in each instance */
export function createVertexInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
function createVertexInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
const { groupCount, instanceCount, stride } = locationIt;
const count = instanceCount * groupCount;
const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
@@ -138,3 +155,34 @@ export function createVertexInstanceColor(locationIt: LocationIterator, color: L
}
return createTextureColor(colors, 'vertexInstance', colorData);
}
function updatePaletteTexture(palette: ColorTheme.Palette, cell: ValueCell<TextureImage<Uint8Array>>) {
let isSynced = true;
const texture = cell.ref.value;
if (palette.colors.length !== texture.width || texture.filter !== palette.filter) {
isSynced = false;
} else {
const data = texture.array;
let o = 0;
for (const c of palette.colors) {
const [r, g, b] = Color.toRgb(c);
if (data[o++] !== r || data[o++] !== g || data[o++] !== b) {
isSynced = false;
break;
}
}
}
if (isSynced) return;
const array = new Uint8Array(palette.colors.length * 3);
let o = 0;
for (const c of palette.colors) {
const [r, g, b] = Color.toRgb(c);
array[o++] = r;
array[o++] = g;
array[o++] = b;
}
ValueCell.update(cell, { array, height: 1, width: palette.colors.length, filter: palette.filter });
}

View File

@@ -177,7 +177,7 @@ export namespace Spheres {
const counts = { drawCount: spheres.sphereCount * 2 * 3, vertexCount: spheres.sphereCount * 4, groupCount, instanceCount };
const padding = getMaxSize(size) * props.sizeFactor;
const padding = spheres.boundingSphere.radius ? getMaxSize(size) * props.sizeFactor : 0;
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
@@ -222,7 +222,9 @@ export namespace Spheres {
}
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
const padding = getMaxSize(values) * values.uSizeFactor.ref.value;
const padding = spheres.boundingSphere.radius
? getMaxSize(values) * values.uSizeFactor.ref.value
: 0;
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);

View File

@@ -133,7 +133,7 @@ describe('renderer', () => {
scene.add(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
expect(ctx.stats.resourceCounts.texture).toBe(6);
expect(ctx.stats.resourceCounts.texture).toBe(7);
expect(ctx.stats.resourceCounts.vertexArray).toBe(6);
expect(ctx.stats.resourceCounts.program).toBe(6);
expect(ctx.stats.resourceCounts.shader).toBe(12);

View File

@@ -32,6 +32,7 @@ const IsosurfaceSchema = {
uSize: UniformSpec('f'),
uLevels: UniformSpec('f'),
uCount: UniformSpec('f'),
uInvert: UniformSpec('b'),
uGridDim: UniformSpec('v3'),
uGridTexDim: UniformSpec('v3'),
@@ -44,7 +45,7 @@ type IsosurfaceValues = Values<typeof IsosurfaceSchema>
const IsosurfaceName = 'isosurface';
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, packedGroup: boolean): ComputeRenderable<IsosurfaceValues> {
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean): ComputeRenderable<IsosurfaceValues> {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
@@ -56,6 +57,7 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ValueCell.updateIfChanged(v.uSize, Math.pow(2, levels));
ValueCell.updateIfChanged(v.uLevels, levels);
ValueCell.updateIfChanged(v.uCount, count);
ValueCell.updateIfChanged(v.uInvert, invert);
ValueCell.update(v.uGridDim, gridDim);
ValueCell.update(v.uGridTexDim, gridTexDim);
@@ -66,12 +68,12 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ctx.namedComputeRenderables[IsosurfaceName].update();
} else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, packedGroup);
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
}
return ctx.namedComputeRenderables[IsosurfaceName];
}
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, packedGroup: boolean) {
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean) {
// console.log('uSize', Math.pow(2, levels))
const values: IsosurfaceValues = {
...QuadValues,
@@ -85,6 +87,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
uSize: ValueCell.create(Math.pow(2, levels)),
uLevels: ValueCell.create(levels),
uCount: ValueCell.create(count),
uInvert: ValueCell.create(invert),
uGridDim: ValueCell.create(gridDim),
uGridTexDim: ValueCell.create(gridTexDim),
@@ -112,7 +115,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.clearColor(0, 0, 0, 0);
}
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
const { gl, resources, extensions } = ctx;
const { pyramidTex, height, levels, scale, count } = histogramPyramid;
const width = pyramidTex.getWidth();
@@ -167,7 +170,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
groupTexture.attachFramebuffer(framebuffer, 1);
normalTexture.attachFramebuffer(framebuffer, 2);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, packedGroup);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup);
ctx.state.currentRenderItemId = -1;
const { drawBuffers } = ctx.extensions;
@@ -201,7 +204,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
*
* Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
*/
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
// console.time('calcActiveVoxels');
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
// ctx.waitForGpuCommandsCompleteSync();
@@ -213,7 +216,7 @@ export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDi
// console.timeEnd('createHistogramPyramid');
// console.time('createIsosurfaceBuffers');
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, packedGroup, vertexTexture, groupTexture, normalTexture);
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, vertexTexture, groupTexture, normalTexture);
// ctx.waitForGpuCommandsCompleteSync();
// console.timeEnd('createIsosurfaceBuffers');

View File

@@ -185,7 +185,9 @@ export const ColorSchema = {
uColor: UniformSpec('v3', 'material'),
uColorTexDim: UniformSpec('v2'),
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
tPalette: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance']),
dUsePalette: DefineSpec('boolean'),
} as const;
export type ColorSchema = typeof ColorSchema
export type ColorValues = Values<ColorSchema>

View File

@@ -7,6 +7,7 @@
import { Sphere3D } from '../../mol-math/geometry';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
import { TextureFilter } from '../webgl/texture';
export function calculateTextureInfo (n: number, itemSize: number) {
n = Math.max(n, 2); // observed issues with 1 pixel textures
@@ -22,6 +23,7 @@ export interface TextureImage<T extends Uint8Array | Float32Array | Int32Array>
readonly width: number
readonly height: number
readonly flipY?: boolean
readonly filter?: TextureFilter
}
export interface TextureVolume<T extends Uint8Array | Float32Array> {

View File

@@ -14,6 +14,10 @@ export const assign_color_varying = `
vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
#endif
#ifdef dUsePalette
vPaletteV = ((vColor.r * 256.0 * 256.0 * 255.0 + vColor.g * 256.0 * 255.0 + vColor.b * 255.0) - 1.0) / 16777215.0;
#endif
#ifdef dOverpaint
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
#endif

View File

@@ -1,6 +1,8 @@
export const assign_material_color = `
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
#if defined(dUsePalette)
vec4 material = vec4(texture2D(tPalette, vec2(vPaletteV, 0.5)).rgb, uAlpha);
#elif defined(dColorType_uniform)
vec4 material = vec4(uColor, uAlpha);
#elif defined(dColorType_varying)
vec4 material = vec4(vColor.rgb, uAlpha);

View File

@@ -21,4 +21,9 @@ export const color_frag_params = `
varying float vGroup;
varying float vTransparency;
#endif
#ifdef dUsePalette
uniform sampler2D tPalette;
varying float vPaletteV;
#endif
`;

View File

@@ -30,4 +30,8 @@ export const color_vert_params = `
uniform vec2 uTransparencyTexDim;
uniform sampler2D tTransparency;
#endif
#ifdef dUsePalette
varying float vPaletteV;
#endif
`;

View File

@@ -18,6 +18,7 @@ uniform float uIsoValue;
uniform float uLevels;
uniform float uSize;
uniform float uCount;
uniform bool uInvert;
uniform vec3 uGridDim;
uniform vec3 uGridTexDim;
@@ -163,6 +164,13 @@ void main(void) {
// current vertex for the up to 15 MC cases
int currentVertex = vI - idot4(m, starts);
// ensure winding-order is the same for negative and positive iso-levels
if (uInvert) {
int v = imod(currentVertex + 1, 3);
if (v == 1) currentVertex += 2;
else if (v == 0) currentVertex -= 2;
}
// get index into triIndices table
int mcIndex = 16 * int(edgeIndex) + currentVertex;
vec4 mcData = texture2D(tTriIndices, vec2(imod(mcIndex, 64), mcIndex / 64) / 64.);
@@ -273,11 +281,18 @@ void main(void) {
voxelPadded(b1 - c3).a - voxelPadded(b1 + c3).a,
voxelPadded(b1 - c4).a - voxelPadded(b1 + c4).a
));
mat3 normalMatrix = transpose3(inverse3(mat3(uGridTransform)));
gl_FragData[2].xyz = normalMatrix * -vec3(
gl_FragData[2].xyz = -vec3(
n0.x + t * (n0.x - n1.x),
n0.y + t * (n0.y - n1.y),
n0.z + t * (n0.z - n1.z)
);
// ensure normal-direction is the same for negative and positive iso-levels
if (uInvert) {
gl_FragData[2].xyz *= -1.0;
}
// apply normal matrix
gl_FragData[2].xyz *= transpose3(inverse3(mat3(uGridTransform)));
}
`;

View File

@@ -283,6 +283,9 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);
} else if (isTexture2d(data, target, gl)) {
const _filter = data.filter ? getFilter(gl, data.filter) : filter;
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, _filter);
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, _filter);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
if (sub) {
gl.texSubImage2D(target, 0, 0, 0, data.width, data.height, format, type, data.array);

View File

@@ -2,7 +2,7 @@
import { parseSdf } from '../sdf/parser';
const SdfString = `
Mrv1718007121815122D
Mrv1718007121815122D
5 4 0 0 0 0 999 V2000
0.0000 0.8250 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0
@@ -17,7 +17,7 @@ const SdfString = `
M CHG 3 1 -1 3 -1 5 -1
M END
> <DATABASE_ID>
DB14523
0
> <DATABASE_NAME>
drugbank
@@ -112,7 +112,225 @@ Phosphate ion
> <SYNONYMS>
Orthophosphate; Phosphate
$$$$`;
$$$$
Comp 2
5 4 0 0 0 0 999 V2000
0.0000 0.8250 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0
-0.8250 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 -0.8250 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0
0.0000 0.0000 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0
0.8250 0.0000 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0
4 1 1 0 0 0 0
4 2 2 0 0 0 0
4 3 1 0 0 0 0
4 5 1 0 0 0 0
M CHG 3 1 -1 3 -1 5 -1
M END
> <DATABASE_ID>
1
$$$$
2244
-OEChem-04122119123D
21 21 0 0 0 0 0 0 0999 V2000
1.2333 0.5540 0.7792 O 0 0 0 0 0 0 0 0 0 0 0 0
-0.6952 -2.7148 -0.7502 O 0 0 0 0 0 0 0 0 0 0 0 0
0.7958 -2.1843 0.8685 O 0 0 0 0 0 0 0 0 0 0 0 0
1.7813 0.8105 -1.4821 O 0 0 0 0 0 0 0 0 0 0 0 0
-0.0857 0.6088 0.4403 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.7927 -0.5515 0.1244 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.7288 1.8464 0.4133 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.1426 -0.4741 -0.2184 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.0787 1.9238 0.0706 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.7855 0.7636 -0.2453 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.1409 -1.8536 0.1477 C 0 0 0 0 0 0 0 0 0 0 0 0
2.1094 0.6715 -0.3113 C 0 0 0 0 0 0 0 0 0 0 0 0
3.5305 0.5996 0.1635 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.1851 2.7545 0.6593 H 0 0 0 0 0 0 0 0 0 0 0 0
-2.7247 -1.3605 -0.4564 H 0 0 0 0 0 0 0 0 0 0 0 0
-2.5797 2.8872 0.0506 H 0 0 0 0 0 0 0 0 0 0 0 0
-3.8374 0.8238 -0.5090 H 0 0 0 0 0 0 0 0 0 0 0 0
3.7290 1.4184 0.8593 H 0 0 0 0 0 0 0 0 0 0 0 0
4.2045 0.6969 -0.6924 H 0 0 0 0 0 0 0 0 0 0 0 0
3.7105 -0.3659 0.6426 H 0 0 0 0 0 0 0 0 0 0 0 0
-0.2555 -3.5916 -0.7337 H 0 0 0 0 0 0 0 0 0 0 0 0
1 5 1 0 0 0 0
1 12 1 0 0 0 0
2 11 1 0 0 0 0
2 21 1 0 0 0 0
3 11 2 0 0 0 0
4 12 2 0 0 0 0
5 6 1 0 0 0 0
5 7 2 0 0 0 0
6 8 2 0 0 0 0
6 11 1 0 0 0 0
7 9 1 0 0 0 0
7 14 1 0 0 0 0
8 10 1 0 0 0 0
8 15 1 0 0 0 0
9 10 2 0 0 0 0
9 16 1 0 0 0 0
10 17 1 0 0 0 0
12 13 1 0 0 0 0
13 18 1 0 0 0 0
13 19 1 0 0 0 0
13 20 1 0 0 0 0
M END
> <PUBCHEM_COMPOUND_CID>
2244
> <PUBCHEM_CONFORMER_RMSD>
0.6
> <PUBCHEM_CONFORMER_DIVERSEORDER>
1
11
10
3
15
17
13
5
16
7
14
9
8
4
18
6
12
2
> <PUBCHEM_MMFF94_PARTIAL_CHARGES>
18
1 -0.23
10 -0.15
11 0.63
12 0.66
13 0.06
14 0.15
15 0.15
16 0.15
17 0.15
2 -0.65
21 0.5
3 -0.57
4 -0.57
5 0.08
6 0.09
7 -0.15
8 -0.15
9 -0.15
> <PUBCHEM_EFFECTIVE_ROTOR_COUNT>
3
> <PUBCHEM_PHARMACOPHORE_FEATURES>
5
1 2 acceptor
1 3 acceptor
1 4 acceptor
3 2 3 11 anion
6 5 6 7 8 9 10 rings
> <PUBCHEM_HEAVY_ATOM_COUNT>
13
> <PUBCHEM_ATOM_DEF_STEREO_COUNT>
0
> <PUBCHEM_ATOM_UDEF_STEREO_COUNT>
0
> <PUBCHEM_BOND_DEF_STEREO_COUNT>
0
> <PUBCHEM_BOND_UDEF_STEREO_COUNT>
0
> <PUBCHEM_ISOTOPIC_ATOM_COUNT>
0
> <PUBCHEM_COMPONENT_COUNT>
1
> <PUBCHEM_CACTVS_TAUTO_COUNT>
1
> <PUBCHEM_CONFORMER_ID>
000008C400000001
> <PUBCHEM_MMFF94_ENERGY>
39.5952
> <PUBCHEM_FEATURE_SELFOVERLAP>
25.432
> <PUBCHEM_SHAPE_FINGERPRINT>
1 1 18265615372930943622
100427 49 16967750034970055351
12138202 97 18271247217817981012
12423570 1 16692715976000295083
12524768 44 16753525617747228747
12716758 59 18341332292274886536
13024252 1 17968377969333732145
14181834 199 17830728755827362645
14614273 12 18262232214645093005
15207287 21 17703787037639964108
15775835 57 18340488876329928641
16945 1 18271533103414939405
193761 8 17907860604865584321
20645476 183 17677348215414174190
20871998 184 18198632231250704846
21040471 1 18411412921197846465
21501502 16 18123463883164380929
23402539 116 18271795865171824860
23419403 2 13539898140662769886
23552423 10 18048876295495619569
23559900 14 18272369794190581304
241688 4 16179044415907240795
257057 1 17478316999871287486
2748010 2 18339085878070479087
305870 269 18263645056784260212
528862 383 18117272558388284091
53812653 8 18410289211719108569
7364860 26 17910392788380644719
81228 2 18050568744116491203
> <PUBCHEM_SHAPE_MULTIPOLES>
244.06
3.86
2.45
0.89
1.95
1.58
0.15
-1.85
0.38
-0.61
-0.02
0.29
0.01
-0.33
> <PUBCHEM_SHAPE_SELFOVERLAP>
513.037
> <PUBCHEM_SHAPE_VOLUME>
136
> <PUBCHEM_COORDINATE_TYPE>
2
5
10
$$$$
`;
describe('sdf reader', () => {
it('basic', async () => {
@@ -120,14 +338,21 @@ describe('sdf reader', () => {
if (parsed.isError) {
throw new Error(parsed.message);
}
const compound = parsed.result.compounds[0];
const { molFile, dataItems } = compound;
const compound1 = parsed.result.compounds[0];
const compound2 = parsed.result.compounds[1];
const compound3 = parsed.result.compounds[2];
const { molFile, dataItems } = compound1;
const { atoms, bonds } = molFile;
expect(parsed.result.compounds.length).toBe(3);
// number of structures
expect(atoms.count).toBe(5);
expect(bonds.count).toBe(4);
expect(compound2.molFile.atoms.count).toBe(5);
expect(compound2.molFile.bonds.count).toBe(4);
expect(atoms.x.value(0)).toBeCloseTo(0, 0.001);
expect(atoms.y.value(0)).toBeCloseTo(0.8250, 0.0001);
expect(atoms.z.value(0)).toBeCloseTo(0, 0.0001);
@@ -138,12 +363,21 @@ describe('sdf reader', () => {
expect(bonds.order.value(3)).toBe(1);
expect(dataItems.dataHeader.value(0)).toBe('DATABASE_ID');
expect(dataItems.data.value(0)).toBe('DB14523');
expect(dataItems.data.value(0)).toBe('0');
expect(dataItems.dataHeader.value(1)).toBe('DATABASE_NAME');
expect(dataItems.data.value(1)).toBe('drugbank');
expect(dataItems.dataHeader.value(31)).toBe('SYNONYMS');
expect(dataItems.data.value(31)).toBe('Orthophosphate; Phosphate');
expect(compound1.dataItems.data.value(0)).toBe('0');
expect(compound2.dataItems.data.value(0)).toBe('1');
expect(compound3.dataItems.dataHeader.value(2)).toBe('PUBCHEM_CONFORMER_DIVERSEORDER');
expect(compound3.dataItems.data.value(2)).toBe('1\n11\n10\n3\n15\n17\n13\n5\n16\n7\n14\n9\n8\n4\n18\n6\n12\n2');
expect(compound3.dataItems.dataHeader.value(21)).toBe('PUBCHEM_COORDINATE_TYPE');
expect(compound3.dataItems.data.value(21)).toBe('2\n5\n10');
});
});

View File

@@ -1,7 +1,8 @@
/**
* 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 Sebastian Bittrich <sebastian.bittrich@rcsb.org>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Column } from '../../../mol-data/db';
@@ -22,24 +23,36 @@ export interface SdfFile {
}[]
}
const delimiter = '$$$$';
function handleDataItems(tokenizer: Tokenizer): { dataHeader: Column<string>, data: Column<string> } {
const dataHeader = TokenBuilder.create(tokenizer.data, 32);
const data = TokenBuilder.create(tokenizer.data, 32);
let sawHeaderToken = false;
while (tokenizer.position < tokenizer.length) {
const line = Tokenizer.readLine(tokenizer);
if (!!line) {
if (line.startsWith('> <')) {
TokenBuilder.add(dataHeader, tokenizer.tokenStart + 3, tokenizer.tokenEnd - 1);
sawHeaderToken = true;
} else if (sawHeaderToken) {
TokenBuilder.add(data, tokenizer.tokenStart, tokenizer.tokenEnd);
sawHeaderToken = false;
// TODO can there be multiline values?
if (line.startsWith(delimiter)) break;
if (!line) continue;
if (line.startsWith('> <')) {
TokenBuilder.add(dataHeader, tokenizer.tokenStart + 3, tokenizer.tokenEnd - 1);
Tokenizer.markLine(tokenizer);
const start = tokenizer.tokenStart;
let end = tokenizer.tokenEnd;
let added = false;
while (tokenizer.position < tokenizer.length) {
const line2 = Tokenizer.readLine(tokenizer);
if (!line2 || line2.startsWith(delimiter) || line2.startsWith('> <')) {
TokenBuilder.add(data, start, end);
added = true;
break;
}
end = tokenizer.tokenEnd;
}
if (!added) {
TokenBuilder.add(data, start, end);
}
} else {
sawHeaderToken = false;
}
}
@@ -49,9 +62,7 @@ function handleDataItems(tokenizer: Tokenizer): { dataHeader: Column<string>, da
};
}
function handleMolFile(data: string) {
const tokenizer = Tokenizer(data);
function handleMolFile(tokenizer: Tokenizer) {
const title = Tokenizer.readLine(tokenizer).trim();
const program = Tokenizer.readLine(tokenizer).trim();
const comment = Tokenizer.readLine(tokenizer).trim();
@@ -60,6 +71,15 @@ function handleMolFile(data: string) {
const atomCount = +counts.substr(0, 3), bondCount = +counts.substr(3, 3);
if (Number.isNaN(atomCount) || Number.isNaN(bondCount)) {
// try to skip to next molecule
while (tokenizer.position < tokenizer.length) {
const line = Tokenizer.readLine(tokenizer);
if (line.startsWith(delimiter)) break;
}
return;
}
const atoms = handleAtoms(tokenizer, atomCount);
const bonds = handleBonds(tokenizer, bondCount);
const dataItems = handleDataItems(tokenizer);
@@ -70,10 +90,16 @@ function handleMolFile(data: string) {
};
}
const delimiter = '$$$$';
function parseInternal(data: string): Result<SdfFile> {
const result: SdfFile = { compounds: data.split(delimiter).map(d => handleMolFile(d)) };
return Result.success(result);
const tokenizer = Tokenizer(data);
const compounds: SdfFile['compounds'] = [];
while (tokenizer.position < tokenizer.length) {
const c = handleMolFile(tokenizer);
if (c) compounds.push(c);
}
return Result.success({ compounds });
}
export function parseSdf(data: string) {

View File

@@ -54,6 +54,12 @@ const StandardComponents = (function() {
{ id: 'SEC', name: 'SELENOCYSTEINE', type: 'L-peptide linking' },
{ id: 'PYL', name: 'PYRROLYSINE', type: 'L-peptide linking' },
{ id: 'MSE', name: 'SELENOMETHIONINE', type: 'L-peptide linking' },
{ id: 'SEP', name: 'PHOSPHOSERINE', type: 'L-peptide linking' },
{ id: 'TPO', name: 'PHOSPHOTHREONINE', type: 'L-peptide linking' },
{ id: 'PTR', name: 'O-PHOSPHOTYROSINE', type: 'L-peptide linking' },
{ id: 'PCA', name: 'PYROGLUTAMIC ACID', type: 'L-peptide linking' },
{ id: 'A', name: 'ADENOSINE-5\'-MONOPHOSPHATE', type: 'RNA linking' },
{ id: 'C', name: 'CYTIDINE-5\'-MONOPHOSPHATE', type: 'RNA linking' },
{ id: 'T', name: 'THYMIDINE-5\'-MONOPHOSPHATE', type: 'RNA linking' },

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>
*/
@@ -15,7 +15,7 @@ import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mo
import { VisualUpdateState } from '../../../mol-repr/util';
import { PickingId } from '../../../mol-geo/geometry/picking';
import { EmptyLoci, Loci } from '../../../mol-model/loci';
import { Interval, OrderedSet } from '../../../mol-data/int';
import { Interval, OrderedSet, SortedArray } from '../../../mol-data/int';
import { Interactions } from '../interactions/interactions';
import { InteractionsProvider } from '../interactions';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
@@ -35,6 +35,8 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
if (!edgeCount) return Mesh.createEmpty(mesh);
const { child } = structure;
const builderProps = {
linkCount: edgeCount,
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
@@ -63,12 +65,28 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
const sizeB = theme.size.size(l);
return Math.min(sizeA, sizeB) * sizeFactor;
},
ignore: (edgeIndex: number) => edges[edgeIndex].props.flag === InteractionFlag.Filtered
ignore: (edgeIndex: number) => {
if (edges[edgeIndex].props.flag === InteractionFlag.Filtered) return true;
if (child) {
const b = edges[edgeIndex];
const childUnitA = child.unitMap.get(b.unitA);
if (!childUnitA) return true;
const unitA = structure.unitMap.get(b.unitA);
const fA = unitsFeatures.get(b.unitA);
// TODO: check all members
const eA = unitA.elements[fA.members[fA.offsets[b.indexA]]];
if (!SortedArray.has(childUnitA.elements, eA)) return true;
}
return false;
}
};
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * sizeFactor);
m.setBoundingSphere(sphere);
return m;
@@ -80,6 +98,7 @@ export const InteractionsInterUnitParams = {
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
includeParent: PD.Boolean(false),
};
export type InteractionsInterUnitParams = typeof InteractionsInterUnitParams

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 { Unit, Structure, StructureElement } from '../../../mol-model/structure';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Loci, EmptyLoci } from '../../../mol-model/loci';
import { Interval, OrderedSet } from '../../../mol-data/int';
import { Interval, OrderedSet, SortedArray } from '../../../mol-data/int';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
import { PickingId } from '../../../mol-geo/geometry/picking';
@@ -25,6 +25,10 @@ import { Sphere3D } from '../../../mol-math/geometry';
async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<InteractionsIntraUnitParams>, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Mesh.createEmpty(mesh);
const location = StructureElement.Location.create(structure, unit);
const interactions = InteractionsProvider.get(structure).value!;
@@ -51,12 +55,16 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
const sizeB = theme.size.size(location);
return Math.min(sizeA, sizeB) * sizeFactor;
},
ignore: (edgeIndex: number) => flag[edgeIndex] === InteractionFlag.Filtered
ignore: (edgeIndex: number) => (
flag[edgeIndex] === InteractionFlag.Filtered ||
// TODO: check all members
(!!childUnit && !SortedArray.has(childUnit.elements, unit.elements[members[offsets[a[edgeIndex]]]]))
)
};
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * sizeFactor);
m.setBoundingSphere(sphere);
return m;
@@ -68,6 +76,7 @@ export const InteractionsIntraUnitParams = {
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
includeParent: PD.Boolean(false),
};
export type InteractionsIntraUnitParams = typeof InteractionsIntraUnitParams
@@ -147,7 +156,7 @@ function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (int
const { offset } = contacts;
const { offsets: fOffsets, indices: fIndices } = features.elementsIndex;
// TODO when isMarking, all elements of contact features need to be in the loci
// TODO: when isMarking, all elements of contact features need to be in the loci
for (const e of loci.elements) {
const unitIdx = group.unitIndexMap.get(e.unit.id);
if (unitIdx !== undefined) continue;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 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>
*/
@@ -50,5 +50,11 @@ export const InteractionsRepresentationProvider = StructureRepresentationProvide
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
detach: (data) => InteractionsProvider.ref(data, false)
},
getData: (structure: Structure, props: PD.Values<InteractionsParams>) => {
return props.includeParent ? structure.asParent() : structure;
},
mustRecreate: (oldProps: PD.Values<InteractionsParams>, newProps: PD.Values<InteractionsParams>) => {
return oldProps.includeParent !== newProps.includeParent;
}
});

View File

@@ -245,7 +245,9 @@ export const WaterNames = new Set([
export const AminoAcidNamesL = new Set([
'HIS', 'ARG', 'LYS', 'ILE', 'PHE', 'LEU', 'TRP', 'ALA', 'MET', 'PRO', 'CYS',
'ASN', 'VAL', 'GLY', 'SER', 'GLN', 'TYR', 'ASP', 'GLU', 'THR', 'SEC', 'PYL',
'UNK' // unknown amino acid from CCD
'UNK', // unknown amino acid from CCD
'MSE', 'SEP', 'TPO', 'PTR', 'PCA' // common
]);
export const AminoAcidNamesD = new Set([
'DAL', // D-ALANINE

View File

@@ -43,7 +43,7 @@ export function atomicSequence(): StructureQuery {
units.push(unit);
}
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
};
}
@@ -62,7 +62,7 @@ export function water(): StructureQuery {
if (P.entity.type(l) !== 'water') continue;
units.push(unit);
}
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
};
}
@@ -92,7 +92,7 @@ export function atomicHet(): StructureQuery {
units.push(unit);
}
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
};
}
@@ -105,7 +105,7 @@ export function spheres(): StructureQuery {
if (unit.kind !== Unit.Kind.Spheres) continue;
units.push(unit);
}
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
};
}

View File

@@ -35,91 +35,73 @@ import { Trajectory } from '../trajectory';
import { RuntimeContext, Task } from '../../../mol-task';
import { computeStructureBoundary } from './util/boundary';
/** Internal structure state */
type State = {
parent?: Structure,
boundary?: Boundary,
lookup3d?: StructureLookup3D,
interUnitBonds?: InterUnitBonds,
unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup>,
unitSymmetryGroupsIndexMap?: IntMap<number>,
unitsSortedByVolume?: ReadonlyArray<Unit>;
carbohydrates?: Carbohydrates,
models?: ReadonlyArray<Model>,
model?: Model,
masterModel?: Model,
representativeModel?: Model,
uniqueResidueNames?: Set<string>,
uniqueElementSymbols?: Set<ElementSymbol>,
entityIndices?: ReadonlyArray<EntityIndex>,
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
serialMapping?: SerialMapping,
hashCode: number,
transformHash: number,
elementCount: number,
bondCount: number,
uniqueElementCount: number,
atomicResidueCount: number,
polymerResidueCount: number,
polymerGapCount: number,
polymerUnitCount: number,
coordinateSystem: SymmetryOperator,
label: string,
propertyData?: any,
customProps?: CustomProperties
}
class Structure {
/** Maps unit.id to unit */
readonly unitMap: IntMap<Unit>;
/** Maps unit.id to index of unit in units array */
readonly unitIndexMap: IntMap<number>;
/** Array of all units in the structure, sorted by unit.id */
readonly units: ReadonlyArray<Unit>;
private _props: {
parent?: Structure,
boundary?: Boundary,
lookup3d?: StructureLookup3D,
interUnitBonds?: InterUnitBonds,
unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup>,
unitSymmetryGroupsIndexMap?: IntMap<number>,
carbohydrates?: Carbohydrates,
models?: ReadonlyArray<Model>,
model?: Model,
masterModel?: Model,
representativeModel?: Model,
uniqueResidueNames?: Set<string>,
uniqueElementSymbols?: Set<ElementSymbol>,
entityIndices?: ReadonlyArray<EntityIndex>,
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
serialMapping?: SerialMapping,
hashCode: number,
/** Hash based on all unit.id values in the structure, reflecting the units transformation */
transformHash: number,
elementCount: number,
bondCount: number,
uniqueElementCount: number,
atomicResidueCount: number,
polymerResidueCount: number,
polymerGapCount: number,
polymerUnitCount: number,
coordinateSystem: SymmetryOperator,
label: string,
propertyData?: any,
customProps?: CustomProperties
} = {
hashCode: -1,
transformHash: -1,
elementCount: -1,
bondCount: -1,
uniqueElementCount: -1,
atomicResidueCount: -1,
polymerResidueCount: -1,
polymerGapCount: -1,
polymerUnitCount: -1,
coordinateSystem: SymmetryOperator.Default,
label: ''
};
subsetBuilder(isSorted: boolean) {
return new StructureSubsetBuilder(this, isSorted);
}
/** Count of all elements in the structure, i.e. the sum of the elements in the units */
get elementCount() {
return this._props.elementCount;
return this.state.elementCount;
}
/** Count of all bonds (intra- and inter-unit) in the structure */
get bondCount() {
if (this._props.bondCount === -1) {
this._props.bondCount = this.interUnitBonds.edgeCount + Bond.getIntraUnitBondCount(this);
if (this.state.bondCount === -1) {
this.state.bondCount = this.interUnitBonds.edgeCount + Bond.getIntraUnitBondCount(this);
}
return this._props.bondCount;
return this.state.bondCount;
}
get hasCustomProperties() {
return !!this._props.customProps && this._props.customProps.all.length > 0;
return !!this.state.customProps && this.state.customProps.all.length > 0;
}
get customPropertyDescriptors() {
if (!this._props.customProps) this._props.customProps = new CustomProperties();
return this._props.customProps;
if (!this.state.customProps) this.state.customProps = new CustomProperties();
return this.state.customProps;
}
/**
* Property data unique to this instance of the structure.
*/
get currentPropertyData() {
if (!this._props.propertyData) this._props.propertyData = Object.create(null);
return this._props.propertyData;
if (!this.state.propertyData) this.state.propertyData = Object.create(null);
return this.state.propertyData;
}
/**
@@ -131,39 +113,39 @@ class Structure {
/** Count of all polymer residues in the structure */
get polymerResidueCount() {
if (this._props.polymerResidueCount === -1) {
this._props.polymerResidueCount = getPolymerResidueCount(this);
if (this.state.polymerResidueCount === -1) {
this.state.polymerResidueCount = getPolymerResidueCount(this);
}
return this._props.polymerResidueCount;
return this.state.polymerResidueCount;
}
/** Count of all polymer gaps in the structure */
get polymerGapCount() {
if (this._props.polymerGapCount === -1) {
this._props.polymerGapCount = getPolymerGapCount(this);
if (this.state.polymerGapCount === -1) {
this.state.polymerGapCount = getPolymerGapCount(this);
}
return this._props.polymerGapCount;
return this.state.polymerGapCount;
}
get polymerUnitCount() {
if (this._props.polymerUnitCount === -1) {
this._props.polymerUnitCount = getPolymerUnitCount(this);
if (this.state.polymerUnitCount === -1) {
this.state.polymerUnitCount = getPolymerUnitCount(this);
}
return this._props.polymerUnitCount;
return this.state.polymerUnitCount;
}
get uniqueElementCount() {
if (this._props.uniqueElementCount === -1) {
this._props.uniqueElementCount = getUniqueElementCount(this);
if (this.state.uniqueElementCount === -1) {
this.state.uniqueElementCount = getUniqueElementCount(this);
}
return this._props.uniqueElementCount;
return this.state.uniqueElementCount;
}
get atomicResidueCount() {
if (this._props.atomicResidueCount === -1) {
this._props.atomicResidueCount = getAtomicResidueCount(this);
if (this.state.atomicResidueCount === -1) {
this.state.atomicResidueCount = getAtomicResidueCount(this);
}
return this._props.atomicResidueCount;
return this.state.atomicResidueCount;
}
/**
@@ -179,15 +161,15 @@ class Structure {
}
get hashCode() {
if (this._props.hashCode !== -1) return this._props.hashCode;
if (this.state.hashCode !== -1) return this.state.hashCode;
return this.computeHash();
}
/** Hash based on all unit.id values in the structure, reflecting the units transformation */
get transformHash() {
if (this._props.transformHash !== -1) return this._props.transformHash;
this._props.transformHash = hashFnv32a(this.units.map(u => u.id));
return this._props.transformHash;
if (this.state.transformHash !== -1) return this.state.transformHash;
this.state.transformHash = hashFnv32a(this.units.map(u => u.id));
return this.state.transformHash;
}
private computeHash() {
@@ -200,7 +182,7 @@ class Structure {
hash = (31 * hash + this.elementCount) | 0;
hash = hash1(hash);
if (hash === -1) hash = 0;
this._props.hashCode = hash;
this.state.hashCode = hash;
return hash;
}
@@ -211,12 +193,12 @@ class Structure {
/** The parent or itself in case this is the root */
get root() {
return this._props.parent || this;
return this.state.parent || this;
}
/** The root/top-most parent or `undefined` in case this is the root */
get parent() {
return this._props.parent;
return this.state.parent;
}
/**
@@ -227,74 +209,74 @@ class Structure {
* by the consumer.
*/
get coordinateSystem(): SymmetryOperator {
return this._props.coordinateSystem;
return this.state.coordinateSystem;
}
get label() {
return this._props.label;
return this.state.label;
}
get boundary() {
if (this._props.boundary) return this._props.boundary;
this._props.boundary = computeStructureBoundary(this);
return this._props.boundary;
if (this.state.boundary) return this.state.boundary;
this.state.boundary = computeStructureBoundary(this);
return this.state.boundary;
}
get lookup3d() {
if (this._props.lookup3d) return this._props.lookup3d;
this._props.lookup3d = new StructureLookup3D(this);
return this._props.lookup3d;
if (this.state.lookup3d) return this.state.lookup3d;
this.state.lookup3d = new StructureLookup3D(this);
return this.state.lookup3d;
}
get interUnitBonds() {
if (this._props.interUnitBonds) return this._props.interUnitBonds;
this._props.interUnitBonds = computeInterUnitBonds(this);
return this._props.interUnitBonds;
if (this.state.interUnitBonds) return this.state.interUnitBonds;
this.state.interUnitBonds = computeInterUnitBonds(this);
return this.state.interUnitBonds;
}
get unitSymmetryGroups(): ReadonlyArray<Unit.SymmetryGroup> {
if (this._props.unitSymmetryGroups) return this._props.unitSymmetryGroups;
this._props.unitSymmetryGroups = StructureSymmetry.computeTransformGroups(this);
return this._props.unitSymmetryGroups;
if (this.state.unitSymmetryGroups) return this.state.unitSymmetryGroups;
this.state.unitSymmetryGroups = StructureSymmetry.computeTransformGroups(this);
return this.state.unitSymmetryGroups;
}
/** Maps unit.id to index of SymmetryGroup in unitSymmetryGroups array */
get unitSymmetryGroupsIndexMap(): IntMap<number> {
if (this._props.unitSymmetryGroupsIndexMap) return this._props.unitSymmetryGroupsIndexMap;
this._props.unitSymmetryGroupsIndexMap = Unit.SymmetryGroup.getUnitSymmetryGroupsIndexMap(this.unitSymmetryGroups);
return this._props.unitSymmetryGroupsIndexMap;
if (this.state.unitSymmetryGroupsIndexMap) return this.state.unitSymmetryGroupsIndexMap;
this.state.unitSymmetryGroupsIndexMap = Unit.SymmetryGroup.getUnitSymmetryGroupsIndexMap(this.unitSymmetryGroups);
return this.state.unitSymmetryGroupsIndexMap;
}
get carbohydrates(): Carbohydrates {
if (this._props.carbohydrates) return this._props.carbohydrates;
this._props.carbohydrates = computeCarbohydrates(this);
return this._props.carbohydrates;
if (this.state.carbohydrates) return this.state.carbohydrates;
this.state.carbohydrates = computeCarbohydrates(this);
return this.state.carbohydrates;
}
get models(): ReadonlyArray<Model> {
if (this._props.models) return this._props.models;
this._props.models = getModels(this);
return this._props.models;
if (this.state.models) return this.state.models;
this.state.models = getModels(this);
return this.state.models;
}
get uniqueResidueNames() {
return this._props.uniqueResidueNames
|| (this._props.uniqueResidueNames = getUniqueResidueNames(this));
return this.state.uniqueResidueNames
|| (this.state.uniqueResidueNames = getUniqueResidueNames(this));
}
get uniqueElementSymbols() {
return this._props.uniqueElementSymbols
|| (this._props.uniqueElementSymbols = getUniqueElementSymbols(this));
return this.state.uniqueElementSymbols
|| (this.state.uniqueElementSymbols = getUniqueElementSymbols(this));
}
get entityIndices() {
return this._props.entityIndices
|| (this._props.entityIndices = getEntityIndices(this));
return this.state.entityIndices
|| (this.state.entityIndices = getEntityIndices(this));
}
get uniqueAtomicResidueIndices() {
return this._props.uniqueAtomicResidueIndices
|| (this._props.uniqueAtomicResidueIndices = getUniqueAtomicResidueIndices(this));
return this.state.uniqueAtomicResidueIndices
|| (this.state.uniqueAtomicResidueIndices = getUniqueAtomicResidueIndices(this));
}
/** Contains only atomic units */
@@ -329,7 +311,7 @@ class Structure {
* to address elements in a structure.
*/
get serialMapping() {
return this._props.serialMapping || (this._props.serialMapping = getSerialMapping(this));
return this.state.serialMapping || (this.state.serialMapping = getSerialMapping(this));
}
/**
@@ -337,25 +319,25 @@ class Structure {
* Otherwise throw an exception.
*/
get model(): Model {
if (this._props.model) return this._props.model;
if (this._props.representativeModel) return this._props.representativeModel;
if (this._props.masterModel) return this._props.masterModel;
if (this.state.model) return this.state.model;
if (this.state.representativeModel) return this.state.representativeModel;
if (this.state.masterModel) return this.state.masterModel;
const models = this.models;
if (models.length > 1) {
throw new Error('The structure is based on multiple models and has neither a master- nor a representative-model.');
}
this._props.model = models[0];
return this._props.model;
this.state.model = models[0];
return this.state.model;
}
/** The master-model, other models can have bonds to it */
get masterModel(): Model | undefined {
return this._props.masterModel;
return this.state.masterModel;
}
/** A representative model, e.g. the first model of a trajectory */
get representativeModel(): Model | undefined {
return this._props.representativeModel;
return this.state.representativeModel;
}
hasElement(e: StructureElement.Location) {
@@ -379,51 +361,39 @@ class Structure {
}
return Structure.create(units, {
label: this.label,
interUnitBonds: this._props.interUnitBonds,
interUnitBonds: this.state.interUnitBonds,
});
}
private initUnits(units: ArrayLike<Unit>) {
const unitMap = IntMap.Mutable<Unit>();
const unitIndexMap = IntMap.Mutable<number>();
let elementCount = 0;
let isSorted = true;
let lastId = units.length > 0 ? units[0].id : 0;
for (let i = 0, _i = units.length; i < _i; i++) {
const u = units[i];
unitMap.set(u.id, u);
elementCount += u.elements.length;
if (u.id < lastId) isSorted = false;
lastId = u.id;
}
if (!isSorted) sort(units, 0, units.length, cmpUnits, arraySwap);
for (let i = 0, _i = units.length; i < _i; i++) {
unitIndexMap.set(units[i].id, i);
}
this._props.elementCount = elementCount;
return { unitMap, unitIndexMap };
private _child: Structure | undefined;
private _target: Structure | undefined;
/**
* For `structure` with `parent` this returns a proxy that
* targets `parent` and has `structure` attached as a child.
*/
asParent(): Structure {
return this.parent ? new Structure(this.parent.units, this.parent.unitMap, this.parent.unitIndexMap, this.parent.state, { child: this, target: this.parent }) : this;
}
constructor(units: ArrayLike<Unit>, props: Structure.Props = {}) {
const { unitMap, unitIndexMap } = this.initUnits(units);
this.unitMap = unitMap;
this.unitIndexMap = unitIndexMap;
this.units = units as ReadonlyArray<Unit>;
get child(): Structure | undefined {
return this._child;
}
if (props.parent) this._props.parent = props.parent.parent || props.parent;
if (props.interUnitBonds) this._props.interUnitBonds = props.interUnitBonds;
/** Get the proxy target. Usefull for equality checks. */
get target(): Structure {
return this._target ?? this;
}
if (props.coordinateSystem) this._props.coordinateSystem = props.coordinateSystem;
else if (props.parent) this._props.coordinateSystem = props.parent.coordinateSystem;
if (props.label) this._props.label = props.label;
else if (props.parent) this._props.label = props.parent.label;
if (props.masterModel) this._props.masterModel = props.masterModel;
else if (props.parent) this._props.masterModel = props.parent.masterModel;
if (props.representativeModel) this._props.representativeModel = props.representativeModel;
else if (props.parent) this._props.representativeModel = props.parent.representativeModel;
/**
* @param units Array of all units in the structure, sorted by unit.id
* @param unitMap Maps unit.id to index of unit in units array
* @param unitIndexMap Array of all units in the structure, sorted by unit.id
*/
constructor(readonly units: ReadonlyArray<Unit>, readonly unitMap: IntMap<Unit>, readonly unitIndexMap: IntMap<number>, private readonly state: State, asParent?: { child: Structure, target: Structure }) {
// always assign to ensure object shape
this._child = asParent?.child;
this._target = asParent?.target;
}
}
@@ -625,7 +595,7 @@ function getSerialMapping(structure: Structure): SerialMapping {
}
namespace Structure {
export const Empty = new Structure([]);
export const Empty = create([]);
export interface Props {
parent?: Structure
@@ -638,7 +608,7 @@ namespace Structure {
representativeModel?: Model
}
/** Serial index of an element in the structure accross all units */
/** Serial index of an element in the structure across all units */
export type SerialIndex = { readonly '@type': 'serial-index' } & number
/** Represents a single structure */
@@ -679,8 +649,57 @@ namespace Structure {
return Loci(structure);
}
export function create(units: ReadonlyArray<Unit>, props?: Props): Structure {
return new Structure(units, props);
export function create(units: ReadonlyArray<Unit>, props: Props = {}): Structure {
// init units
const unitMap = IntMap.Mutable<Unit>();
const unitIndexMap = IntMap.Mutable<number>();
let elementCount = 0;
let isSorted = true;
let lastId = units.length > 0 ? units[0].id : 0;
for (let i = 0, _i = units.length; i < _i; i++) {
const u = units[i];
unitMap.set(u.id, u);
elementCount += u.elements.length;
if (u.id < lastId) isSorted = false;
lastId = u.id;
}
if (!isSorted) sort(units, 0, units.length, cmpUnits, arraySwap);
for (let i = 0, _i = units.length; i < _i; i++) {
unitIndexMap.set(units[i].id, i);
}
// initial state
const state: State = {
hashCode: -1,
transformHash: -1,
elementCount,
bondCount: -1,
uniqueElementCount: -1,
atomicResidueCount: -1,
polymerResidueCount: -1,
polymerGapCount: -1,
polymerUnitCount: -1,
coordinateSystem: SymmetryOperator.Default,
label: ''
};
// handle props
if (props.parent) state.parent = props.parent.parent || props.parent;
if (props.interUnitBonds) state.interUnitBonds = props.interUnitBonds;
if (props.coordinateSystem) state.coordinateSystem = props.coordinateSystem;
else if (props.parent) state.coordinateSystem = props.parent.coordinateSystem;
if (props.label) state.label = props.label;
else if (props.parent) state.label = props.parent.label;
if (props.masterModel) state.masterModel = props.masterModel;
else if (props.parent) state.masterModel = props.parent.masterModel;
if (props.representativeModel) state.representativeModel = props.representativeModel;
else if (props.parent) state.representativeModel = props.parent.representativeModel;
return new Structure(units, unitMap, unitIndexMap, state);
}
export async function ofTrajectory(trajectory: Trajectory, ctx: RuntimeContext): Promise<Structure> {
@@ -886,7 +905,7 @@ namespace Structure {
const cs = s.coordinateSystem;
const newCS = SymmetryOperator.compose(SymmetryOperator.create(cs.name, transform, cs), cs);
return new Structure(units, { parent: s, coordinateSystem: newCS });
return create(units, { parent: s, coordinateSystem: newCS });
}
export class StructureBuilder {
@@ -1148,6 +1167,57 @@ namespace Structure {
}
}
export interface ForEachAtomicHierarchyElementParams {
// Called for 1st element of each chain
// Note that chains can be split, meaning each chain would be called multiple times.
chain?: (e: StructureElement.Location<Unit.Atomic>) => void,
// Called for 1st element of each residue
residue?: (e: StructureElement.Location<Unit.Atomic>) => void,
// Called for each element
atom?: (e: StructureElement.Location<Unit.Atomic>) => void,
};
export function eachAtomicHierarchyElement(structure: Structure, { chain, residue, atom }: ForEachAtomicHierarchyElementParams) {
const l = StructureElement.Location.create<Unit.Atomic>(structure);
for (const unit of structure.units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
l.unit = unit;
const { elements } = unit;
const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
while (chainsIt.hasNext) {
const chainSegment = chainsIt.move();
if (chain) {
l.element = elements[chainSegment.start];
chain(l);
}
if (!residue && !atom) continue;
residuesIt.setSegment(chainSegment);
while (residuesIt.hasNext) {
const residueSegment = residuesIt.move();
if (residue) {
l.element = elements[residueSegment.start];
residue(l);
}
if (!atom) continue;
for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
l.element = elements[j];
atom(l);
}
}
}
}
}
//
export const DefaultSizeThresholds = {

View File

@@ -6,12 +6,13 @@
import { PluginCommands } from '../../../mol-plugin/commands';
import { StateTransform } from '../../../mol-state';
import { shallowEqual } from '../../../mol-util';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { PluginStateAnimation } from '../model';
export const AnimateStateInterpolation = PluginStateAnimation.create({
name: 'built-in.animate-state-interpolation',
display: { name: 'Animate State Interpolation' },
display: { name: 'Animate State (experimental)' },
params: () => ({
transtionDurationInMs: PD.Numeric(2000, { min: 100, max: 30000, step: 10 })
}),
@@ -42,15 +43,25 @@ export const AnimateStateInterpolation = PluginStateAnimation.create({
for (const s of src) {
for (const t of tar) {
// TODO: better than quadratic alg.
// TODO: support for adding/removing nodes
if (t.ref !== s.ref) continue;
if (t.version === s.version) continue;
const e = StateTransform.fromJSON(s), f = StateTransform.fromJSON(t);
const oldState = state.cells.get(s.ref);
if (!oldState) continue;
let newState;
if (!e.transformer.definition.interpolate) {
update.to(s.ref).update(currentT <= 0.5 ? e.params : f.params);
newState = currentT <= 0.5 ? e.params : f.params;
} else {
update.to(s.ref).update(e.transformer.definition.interpolate(e.params, f.params, currentT, ctx.plugin));
newState = e.transformer.definition.interpolate(e.params, f.params, currentT, ctx.plugin);
}
if (!shallowEqual(oldState, newState)) {
update.to(s.ref).update(newState);
}
}
}

View File

@@ -123,14 +123,23 @@ export const GroProvider: TrajectoryFormatProvider = {
};
export const MolProvider: TrajectoryFormatProvider = {
label: 'MOL/SDF',
description: 'MOL/SDF',
label: 'MOL',
description: 'MOL',
category: TrajectoryFormatCategory,
stringExtensions: ['mol', 'sdf', 'sd'],
stringExtensions: ['mol'],
parse: directTrajectory(StateTransforms.Model.TrajectoryFromMOL),
visuals: defaultVisuals
};
export const SdfProvider: TrajectoryFormatProvider = {
label: 'SDF',
description: 'SDF',
category: TrajectoryFormatCategory,
stringExtensions: ['sdf', 'sd'],
parse: directTrajectory(StateTransforms.Model.TrajectoryFromSDF),
visuals: defaultVisuals
};
export const Mol2Provider: TrajectoryFormatProvider = {
label: 'MOL2',
description: 'MOL2',
@@ -148,6 +157,7 @@ export const BuiltInTrajectoryFormats = [
['gro', GroProvider] as const,
['xyz', XyzProvider] as const,
['mol', MolProvider] as const,
['sdf', SdfProvider] as const,
['mol2', Mol2Provider] as const,
] as const;

View File

@@ -21,7 +21,7 @@ export async function setStructureClipping(plugin: PluginContext, components: St
await eachRepr(plugin, components, async (update, repr, clippingCell) => {
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
const structure = repr.obj!.data.source.data;
const structure = repr.obj!.data.sourceData;
// always use the root structure to get the loci so the clipping
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);

View File

@@ -22,7 +22,7 @@ export async function setStructureOverpaint(plugin: PluginContext, components: S
await eachRepr(plugin, components, async (update, repr, overpaintCell) => {
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
const structure = repr.obj!.data.source.data;
const structure = repr.obj!.data.sourceData;
// always use the root structure to get the loci so the overpaint
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);

View File

@@ -21,7 +21,7 @@ export async function setStructureTransparency(plugin: PluginContext, components
await eachRepr(plugin, components, async (update, repr, transparencyCell) => {
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
const structure = repr.obj!.data.source.data;
const structure = repr.obj!.data.sourceData;
// always use the root structure to get the loci so the transparency
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);

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>
@@ -12,7 +12,7 @@ import { StatefulPluginComponent } from '../component';
import { PluginContext } from '../../mol-plugin/context';
import { utf8ByteCount, utf8Write } from '../../mol-io/common/utf8';
import { Asset } from '../../mol-util/assets';
import { zip } from '../../mol-util/zip/zip';
import { Zip } from '../../mol-util/zip/zip';
import { readFromFile } from '../../mol-util/data-source';
import { objectForEach } from '../../mol-util/object';
import { PLUGIN_VERSION } from '../../mol-plugin/version';
@@ -217,7 +217,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
zipDataObj['assets.json'] = data;
}
const zipFile = zip(zipDataObj);
const zipFile = await this.plugin.runTask(Zip(zipDataObj));
return new Blob([zipFile], {type : 'application/zip'});
}
}

View File

@@ -41,8 +41,8 @@ export namespace PluginStateObject {
return !!o && o.type.typeClass === 'Behavior';
}
export interface Representation3DData<T extends Representation.Any, S extends StateObject = StateObject> { repr: T, source: S }
export function CreateRepresentation3D<T extends Representation.Any, S extends StateObject = StateObject>(type: { name: string }) {
export interface Representation3DData<T extends Representation.Any, S = any> { repr: T, sourceData: S }
export function CreateRepresentation3D<T extends Representation.Any, S = any>(type: { name: string }) {
return Create<Representation3DData<T, S>>({ ...type, typeClass: 'Representation3D' });
}
@@ -102,10 +102,10 @@ export namespace PluginStateObject {
export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
export namespace Structure {
export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any> | ShapeRepresentation<any, any, any>, Structure>({ name: 'Structure 3D' }) { }
export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any>, _Structure>({ name: 'Structure 3D' }) { }
export interface Representation3DStateData {
source: Representation3D,
repr: StructureRepresentation<any>,
/** used to restore state when the obj is removed */
initialState: Partial<StructureRepresentationState>,
state: Partial<StructureRepresentationState>,
@@ -120,12 +120,12 @@ export namespace PluginStateObject {
export namespace Volume {
export class Data extends Create<_Volume>({ name: 'Volume', typeClass: 'Object' }) { }
export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>, _Volume>({ name: 'Volume 3D' }) { }
}
export namespace Shape {
export class Provider extends Create<ShapeProvider<any, any, any>>({ name: 'Shape Provider', typeClass: 'Object' }) { }
export class Representation3D extends CreateRepresentation3D<ShapeRepresentation<any, any, any>>({ name: 'Shape 3D' }) { }
export class Representation3D extends CreateRepresentation3D<ShapeRepresentation<any, any, any>, unknown>({ name: 'Shape 3D' }) { }
}
}

View File

@@ -39,6 +39,7 @@ import { parseXtc } from '../../mol-io/reader/xtc/parser';
import { coordinatesFromXtc } from '../../mol-model-formats/structure/xtc';
import { parseXyz } from '../../mol-io/reader/xyz/parser';
import { trajectoryFromXyz } from '../../mol-model-formats/structure/xyz';
import { parseSdf } from '../../mol-io/reader/sdf/parser';
export { CoordinatesFromDcd };
export { CoordinatesFromXtc };
@@ -50,6 +51,7 @@ export { TrajectoryFromPDB };
export { TrajectoryFromGRO };
export { TrajectoryFromXYZ };
export { TrajectoryFromMOL };
export { TrajectoryFromSDF };
export { TrajectoryFromMOL2 };
export { TrajectoryFromCube };
export { TrajectoryFromCifCore };
@@ -292,6 +294,36 @@ const TrajectoryFromMOL = PluginStateTransform.BuiltIn({
}
});
type TrajectoryFromSDF = typeof TrajectoryFromSDF
const TrajectoryFromSDF = PluginStateTransform.BuiltIn({
name: 'trajectory-from-sdf',
display: { name: 'Parse SDF', description: 'Parse SDF string and create trajectory.' },
from: [SO.Data.String],
to: SO.Molecule.Trajectory
})({
apply({ a }) {
return Task.create('Parse SDF', async ctx => {
const parsed = await parseSdf(a.data).runInContext(ctx);
if (parsed.isError) throw new Error(parsed.message);
const models: Model[] = [];
for (const { molFile } of parsed.result.compounds) {
const traj = await trajectoryFromMol(molFile).runInContext(ctx);
for (let i = 0; i < traj.frameCount; i++) {
models.push(await Task.resolveInContext(traj.getFrameAtIndex(i), ctx));
}
}
const traj = new ArrayTrajectory(models);
const props = trajectoryProps(traj);
return new SO.Molecule.Trajectory(traj, props);
});
}
});
type TrajectoryFromMOL2 = typeof TrajectoryFromMOL
const TrajectoryFromMOL2 = PluginStateTransform.BuiltIn({
name: 'trajectory-from-mol2',
@@ -382,6 +414,10 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
return new SO.Molecule.Model(model, { label, description });
});
},
interpolate(a, b, t) {
const modelIndex = t >= 1 ? b.modelIndex : a.modelIndex + Math.floor((b.modelIndex - a.modelIndex + 1) * t);
return { modelIndex };
},
dispose({ b }) {
b?.data.customProperties.dispose();
}

View File

@@ -130,14 +130,15 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
return Task.create('Structure Representation', async ctx => {
const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
const provider = plugin.representation.structure.registry.get(params.type.name);
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
const data = provider.getData?.(a.data, params.type.params) || a.data;
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, provider.getParams);
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, params);
repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, params));
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, params);
repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, params));
const props = params.type.params || {};
await repr.createOrUpdate(props, a.data).runInContext(ctx);
return new SO.Molecule.Structure.Representation3D({ repr, source: a }, { label: provider.label });
await repr.createOrUpdate(props, data).runInContext(ctx);
return new SO.Molecule.Structure.Representation3D({ repr, sourceData: a.data }, { label: provider.label });
});
},
update({ a, b, oldParams, newParams, cache }, plugin: PluginContext) {
@@ -145,26 +146,28 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
const provider = plugin.representation.structure.registry.get(newParams.type.name);
if (provider.mustRecreate?.(oldParams.type.params, newParams.type.params)) return StateTransformer.UpdateResult.Recreate;
const data = provider.getData?.(a.data, newParams.type.params) || a.data;
const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
// TODO: if themes had a .needsUpdate method the following block could
// be optimized and only executed conditionally
// dispose isn't called on update so we need to handle it manually
Theme.releaseDependencies(plugin.representation.structure.themes, { structure: b.data.source.data }, oldParams);
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, newParams);
b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, newParams));
Theme.releaseDependencies(plugin.representation.structure.themes, { structure: b.data.sourceData }, oldParams);
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, newParams);
b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, newParams));
const props = { ...b.data.repr.props, ...newParams.type.params };
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.source = a;
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.sourceData = a.data;
return StateTransformer.UpdateResult.Updated;
});
},
dispose({ b, params }, plugin: PluginContext) {
if (!b || !params) return;
const structure = b.data.source.data;
const structure = b.data.sourceData;
const provider = plugin.representation.structure.registry.get(params.type.name);
if (provider.ensureCustomProperties) provider.ensureCustomProperties.detach(structure);
Theme.releaseDependencies(plugin.representation.structure.themes, { structure }, params);
@@ -195,26 +198,26 @@ const UnwindStructureAssemblyRepresentation3D = PluginStateTransform.BuiltIn({
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const unitTransforms = new StructureUnitTransforms(structure);
unwindStructureAssembly(structure, unitTransforms, params.t);
return new SO.Molecule.Structure.Representation3DState({
state: { unitTransforms },
initialState: { unitTransforms: new StructureUnitTransforms(structure) },
info: structure,
source: a
repr: a.data.repr
}, { label: `Unwind T = ${params.t.toFixed(2)}` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
const unitTransforms = b.data.state.unitTransforms!;
unwindStructureAssembly(structure, unitTransforms, newParams.t);
b.label = `Unwind T = ${newParams.t.toFixed(2)}`;
b.data.source = a;
b.data.repr = a.data.repr;
return StateTransformer.UpdateResult.Updated;
}
});
@@ -232,26 +235,26 @@ const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const unitTransforms = new StructureUnitTransforms(structure.root);
explodeStructure(structure, unitTransforms, params.t);
return new SO.Molecule.Structure.Representation3DState({
state: { unitTransforms },
initialState: { unitTransforms: new StructureUnitTransforms(structure.root) },
info: structure.root,
source: a
repr: a.data.repr
}, { label: `Explode T = ${params.t.toFixed(2)}` });
},
update({ a, b, newParams, oldParams }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
if (b.data.info !== structure.root) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
const unitTransforms = b.data.state.unitTransforms!;
explodeStructure(structure.root, unitTransforms, newParams.t);
b.label = `Explode T = ${newParams.t.toFixed(2)}`;
b.data.source = a;
b.data.repr = a.data.repr;
return StateTransformer.UpdateResult.Updated;
}
});
@@ -280,28 +283,28 @@ const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltI
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const overpaint = Overpaint.ofScript(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { overpaint },
initialState: { overpaint: Overpaint.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const oldStructure = b.data.info as Structure;
const newStructure = a.data.source.data;
const newStructure = a.data.sourceData;
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldOverpaint = b.data.state.overpaint!;
const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.overpaint = newOverpaint;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -332,28 +335,28 @@ const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltI
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const overpaint = Overpaint.ofBundle(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { overpaint },
initialState: { overpaint: Overpaint.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const oldStructure = b.data.info as Structure;
const newStructure = a.data.source.data;
const newStructure = a.data.sourceData;
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldOverpaint = b.data.state.overpaint!;
const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.overpaint = newOverpaint;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -381,27 +384,27 @@ const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.Bui
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const transparency = Transparency.ofScript(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { transparency },
initialState: { transparency: Transparency.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Transparency (${transparency.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldTransparency = b.data.state.transparency!;
const newTransparency = Transparency.ofScript(newParams.layers, structure);
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.transparency = newTransparency;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -430,27 +433,27 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const transparency = Transparency.ofBundle(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { transparency },
initialState: { transparency: Transparency.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Transparency (${transparency.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldTransparency = b.data.state.transparency!;
const newTransparency = Transparency.ofBundle(newParams.layers, structure);
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.transparency = newTransparency;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -478,27 +481,27 @@ const ClippingStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const clipping = Clipping.ofScript(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { clipping },
initialState: { clipping: Clipping.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Clipping (${clipping.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldClipping = b.data.state.clipping!;
const newClipping = Clipping.ofScript(newParams.layers, structure);
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.clipping = newClipping;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Clipping (${newClipping.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -527,27 +530,27 @@ const ClippingStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const structure = a.data.sourceData;
const clipping = Clipping.ofBundle(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { clipping },
initialState: { clipping: Clipping.Empty },
info: structure,
source: a
repr: a.data.repr
}, { label: `Clipping (${clipping.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
const oldClipping = b.data.state.clipping!;
const newClipping = Clipping.ofBundle(newParams.layers, structure);
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.clipping = newClipping;
b.data.source = a;
b.data.repr = a.data.repr;
b.label = `Clipping (${newClipping.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
@@ -650,7 +653,7 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
const props = params.type.params || {};
await repr.createOrUpdate(props, a.data).runInContext(ctx);
return new SO.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
return new SO.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -663,6 +666,7 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams.type.params };
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.sourceData = a.data;
b.description = VolumeRepresentation3DHelpers.getDescription(props);
return StateTransformer.UpdateResult.Updated;
});
@@ -690,13 +694,14 @@ const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
const props = { ...PD.getDefaultValues(a.data.params), ...params };
const repr = ShapeRepresentation(a.data.getShape, a.data.geometryUtils);
await repr.createOrUpdate(props, a.data.data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: a.data.label });
return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: a.data.label });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
return Task.create('Shape Representation', async ctx => {
const props = { ...b.data.repr.props, ...newParams };
await b.data.repr.createOrUpdate(props, a.data.data).runInContext(ctx);
b.data.sourceData = a.data;
return StateTransformer.UpdateResult.Updated;
});
}
@@ -724,7 +729,7 @@ const ModelUnitcell3D = PluginStateTransform.BuiltIn({
const data = getUnitcellData(a.data, symmetry, params);
const repr = UnitcellRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => UnitcellParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Unit Cell`, description: symmetry.spacegroup.name });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Unit Cell`, description: symmetry.spacegroup.name });
});
},
update({ a, b, newParams }) {
@@ -734,7 +739,7 @@ const ModelUnitcell3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getUnitcellData(a.data, symmetry, props);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
}
@@ -763,13 +768,13 @@ const StructureBoundingBox3D = PluginStateTransform.BuiltIn({
return Shape.create('Bouding Box', data, mesh, () => data.color, () => 1, () => 'Bounding Box');
}, Mesh.Utils);
await repr.createOrUpdate(params, { box: a.data.boundary.box, radius: params.radius, color: params.color }).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Bounding Box` });
return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: `Bounding Box` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
return Task.create('Bounding Box', async ctx => {
await b.data.repr.createOrUpdate(newParams, { box: a.data.boundary.box, radius: newParams.radius, color: newParams.color }).runInContext(ctx);
b.data.source = a;
b.data.sourceData = a.data;
return StateTransformer.UpdateResult.Updated;
});
}
@@ -794,7 +799,7 @@ const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
const data = getDistanceDataFromStructureSelections(a.data);
const repr = DistanceRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DistanceParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Distance` });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Distance` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -802,7 +807,7 @@ const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getDistanceDataFromStructureSelections(a.data);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
},
@@ -827,7 +832,7 @@ const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
const data = getAngleDataFromStructureSelections(a.data);
const repr = AngleRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AngleParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Angle` });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Angle` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -835,7 +840,7 @@ const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getAngleDataFromStructureSelections(a.data);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
},
@@ -860,7 +865,7 @@ const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
const data = getDihedralDataFromStructureSelections(a.data);
const repr = DihedralRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DihedralParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Dihedral` });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Dihedral` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -868,7 +873,7 @@ const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getDihedralDataFromStructureSelections(a.data);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
},
@@ -893,7 +898,7 @@ const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
const data = getLabelDataFromStructureSelections(a.data);
const repr = LabelRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => LabelParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Label` });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Label` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -901,7 +906,7 @@ const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getLabelDataFromStructureSelections(a.data);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
},
@@ -926,7 +931,7 @@ const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
const data = getOrientationDataFromStructureSelections(a.data);
const repr = OrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => OrientationParams);
await repr.createOrUpdate(params, data).runInContext(ctx);
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Orientation` });
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Orientation` });
});
},
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
@@ -934,7 +939,7 @@ const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...newParams };
const data = getOrientationDataFromStructureSelections(a.data);
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
b.data.source = a;
b.data.sourceData = data;
return StateTransformer.UpdateResult.Updated;
});
},

View File

@@ -151,7 +151,7 @@ const VolumeFromDensityServerCif = PluginStateTransform.BuiltIn({
const densityServerCif = CIF.schema.densityServer(block);
const volume = await volumeFromDensityServerData(densityServerCif, { entryId: params.entryId }).runInContext(ctx);
const [x, y, z] = volume.grid.cells.space.dimensions;
const props = { label: densityServerCif.volume_data_3d_info.name.value(0), description: `Volume ${x}\u00D7${y}\u00D7${z}` };
const props = { label: params.entryId ?? densityServerCif.volume_data_3d_info.name.value(0), description: `Volume ${x}\u00D7${y}\u00D7${z}` };
return new SO.Volume.Data(volume, props);
});
},

View File

@@ -41,6 +41,9 @@ export function MoleculeSvg() { return _Molecule; }
const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z" /></svg>;
export function CubeOutlineSvg() { return _CubeOutline; }
const _CubeSend = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M16,4L9,8.04V15.96L16,20L23,15.96V8.04M16,6.31L19.8,8.5L16,10.69L12.21,8.5M0,7V9H7V7M11,10.11L15,12.42V17.11L11,14.81M21,10.11V14.81L17,17.11V12.42M2,11V13H7V11M4,15V17H7V15" /></svg>;
export function CubeSendSvg() { return _CubeSend; }
const _CursorDefaultOutline = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M10.07,14.27C10.57,14.03 11.16,14.25 11.4,14.75L13.7,19.74L15.5,18.89L13.19,13.91C12.95,13.41 13.17,12.81 13.67,12.58L13.95,12.5L16.25,12.05L8,5.12V15.9L9.82,14.43L10.07,14.27M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V3A1,1 0 0,1 7,2C7.24,2 7.47,2.09 7.64,2.23L7.65,2.22L19.14,11.86C19.57,12.22 19.62,12.85 19.27,13.27C19.12,13.45 18.91,13.57 18.7,13.61L15.54,14.23L17.74,18.96C18,19.46 17.76,20.05 17.26,20.28L13.64,21.97Z' /></svg>;
export function CursorDefaultOutlineSvg() { return _CursorDefaultOutline; }

View File

@@ -240,8 +240,8 @@ function renderSimple(options: { props: ParamProps<any>, state: { showHelp: bool
const help = props.param.help
? props.param.help(props.value)
: { description: props.param.description, legend: props.param.legend };
const desc = props.param.description;
const hasHelp = help.description || help.legend;
const desc = label + (hasHelp ? '. Click for help.' : '');
return <>
<ControlRow
className={className}

View File

@@ -6,7 +6,6 @@
import { PluginUIComponent } from '../base';
import { StateTransformParameters } from '../state/common';
import * as React from 'react';
import { VolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/behavior';
import { ExpandableControlRow, IconButton } from '../controls/common';
import { ParamDefinition as PD } from '../../mol-util/param-definition';

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import { State } from '../../mol-state';
import { PluginUIComponent } from '../base';
import { Icon, CodeSvg } from '../controls/icons';

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import { PluginUIComponent } from '../base';
import { ParameterControls, ParamOnChange } from '../controls/parameters';
import { Button } from '../controls/common';

View File

@@ -8,7 +8,6 @@ import { State, StateTransform, StateTransformer } from '../../mol-state';
import { memoizeLatest } from '../../mol-util/memoize';
import { StateTransformParameters, TransformControlBase } from './common';
import { Observable } from 'rxjs';
import * as React from 'react';
import { PluginUIComponent } from '../base';
export { UpdateTransformControl, TransformUpdaterControl };

View File

@@ -4,7 +4,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { OrderedSet, SortedArray } from '../../mol-data/int';
import { Structure, StructureElement, StructureProperties, Unit } from '../../mol-model/structure';
import { UnitIndex } from '../../mol-model/structure/structure/element/element';

View File

@@ -10,14 +10,17 @@ import { Loci } from '../../mol-model/loci';
import { StructureElement } from '../../mol-model/structure';
import { StructureMeasurementCell, StructureMeasurementOptions, StructureMeasurementParams } from '../../mol-plugin-state/manager/structure/measurement';
import { StructureSelectionHistoryEntry } from '../../mol-plugin-state/manager/structure/selection';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { PluginCommands } from '../../mol-plugin/commands';
import { AngleData } from '../../mol-repr/shape/loci/angle';
import { DihedralData } from '../../mol-repr/shape/loci/dihedral';
import { DistanceData } from '../../mol-repr/shape/loci/distance';
import { LabelData } from '../../mol-repr/shape/loci/label';
import { angleLabel, dihedralLabel, distanceLabel, lociLabel } from '../../mol-theme/label';
import { FiniteArray } from '../../mol-util/type-helpers';
import { CollapsableControls, PurePluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, ExpandGroup, IconButton, ToggleButton } from '../controls/common';
import { Icon, PencilRulerSvg, SetSvg, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, AddSvg, TuneSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, MoreHorizSvg } from '../controls/icons';
import { AddSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg, PencilRulerSvg, SetSvg, TuneSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg } from '../controls/icons';
import { ParameterControls } from '../controls/parameters';
import { UpdateTransformControl } from '../state/update-transform';
import { ToggleSelectionModeButton } from './selection';
@@ -216,7 +219,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
}
get selections() {
return this.props.cell.obj?.data.source as PluginStateObject.Molecule.Structure.Selections | undefined;
return this.props.cell.obj?.data.sourceData as Partial<DistanceData & AngleData & DihedralData & LabelData> | undefined;
}
delete = () => {
@@ -234,8 +237,8 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
if (!selections) return;
this.plugin.managers.interactivity.lociHighlights.clearHighlights();
for (const d of selections.data) {
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: d.loci }, false);
for (const loci of this.lociArray) {
this.plugin.managers.interactivity.lociHighlights.highlight({ loci }, false);
}
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
}
@@ -250,21 +253,31 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
const selections = this.selections;
if (!selections) return;
const sphere = Loci.getBundleBoundingSphere(toLociBundle(selections.data));
const sphere = Loci.getBundleBoundingSphere({ loci: this.lociArray });
if (sphere) {
this.plugin.managers.camera.focusSphere(sphere);
}
}
private get lociArray(): FiniteArray<Loci> {
const selections = this.selections;
if (!selections) return [];
if (selections.infos) return [selections.infos[0].loci];
if (selections.pairs) return selections.pairs[0].loci;
if (selections.triples) return selections.triples[0].loci;
if (selections.quads) return selections.quads[0].loci;
return [];
}
get label() {
const selections = this.selections;
switch (selections?.data.length) {
case 1: return lociLabel(selections.data[0].loci, { condensed: true });
case 2: return distanceLabel(toLociBundle(selections.data), { condensed: true, unitLabel: this.plugin.managers.structure.measurement.state.options.distanceUnitLabel });
case 3: return angleLabel(toLociBundle(selections.data), { condensed: true });
case 4: return dihedralLabel(toLociBundle(selections.data), { condensed: true });
default: return '';
}
if (!selections) return '<empty>';
if (selections.infos) return lociLabel(selections.infos[0].loci, { condensed: true });
if (selections.pairs) return distanceLabel(selections.pairs[0], { condensed: true, unitLabel: this.plugin.managers.structure.measurement.state.options.distanceUnitLabel });
if (selections.triples) return angleLabel(selections.triples[0], { condensed: true });
if (selections.quads) return dihedralLabel(selections.quads[0], { condensed: true });
return '<empty>';
}
get actions(): ActionMenu.Items {
@@ -302,8 +315,4 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
</>}
</>;
}
}
function toLociBundle(data: FiniteArray<{ loci: Loci }, any>): { loci: FiniteArray<Loci, any> } {
return { loci: (data.map(d => d.loci) as unknown as FiniteArray<Loci, any>) };
}

View File

@@ -5,7 +5,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { Model } from '../../mol-model/structure';
import { ModelRef, StructureHierarchyRef, TrajectoryRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
import { StateTransforms } from '../../mol-plugin-state/transforms';

View File

@@ -4,7 +4,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react';
import { CollapsableControls, PurePluginUIComponent } from '../base';
import { Icon, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, TuneSvg, SuperposeAtomsSvg, SuperposeChainsSvg, SuperpositionSvg } from '../controls/icons';
import { Button, ToggleButton, IconButton } from '../controls/common';

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import { PluginUIComponent } from './base';
import { OrderedMap } from 'immutable';
import { TaskManager } from '../mol-plugin/util/task-manager';

View File

@@ -6,7 +6,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import { PluginUIComponent } from './base';
import { PluginToastManager } from '../mol-plugin/util/toast';
import { IconButton } from './controls/common';

View File

@@ -6,7 +6,6 @@
*/
import { produce } from 'immer';
import * as React from 'react';
import { Canvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransform } from '../../mol-state';

View File

@@ -267,7 +267,7 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
await repr.createOrUpdate(props, channel.data).runInContext(ctx);
if (transform) repr.setState({ transform });
return new SO.Volume.Representation3D({ repr, source: a }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
return new SO.Volume.Representation3D({ repr, sourceData: channel.data }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
}),
update: ({ a, b, newParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
// TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work
@@ -280,6 +280,7 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...params.type.params };
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
await b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx);
b.data.sourceData = channel.data;
// TODO: set the transform here as well in case the structure moves?
// doing this here now breaks the code for some reason...

View File

@@ -54,20 +54,20 @@ export function SyncStructureRepresentation3DState(ctx: PluginContext) {
events.object.created.subscribe(e => {
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
data.source.data.repr.setState(data.state);
ctx.canvas3d?.update(data.source.data.repr);
data.repr.setState(data.state);
ctx.canvas3d?.update(data.repr);
});
events.object.updated.subscribe(e => {
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
data.source.data.repr.setState(data.state);
ctx.canvas3d?.update(data.source.data.repr);
data.repr.setState(data.state);
ctx.canvas3d?.update(data.repr);
});
events.object.removed.subscribe(e => {
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
data.source.data.repr.setState(data.initialState);
ctx.canvas3d?.update(data.source.data.repr);
data.repr.setState(data.initialState);
ctx.canvas3d?.update(data.repr);
});
}

View File

@@ -21,6 +21,7 @@ import { StateActions } from '../mol-plugin-state/actions';
import { AssignColorVolume } from '../mol-plugin-state/actions/volume';
import { StateTransforms } from '../mol-plugin-state/transforms';
import { BoxifyVolumeStreaming, CreateVolumeStreamingBehavior, InitVolumeStreaming } from '../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { AnimateStateInterpolation } from '../mol-plugin-state/animation/built-in/state-interpolation';
export { PluginSpec };
@@ -126,6 +127,7 @@ export const DefaultPluginSpec = (): PluginSpec => ({
AnimateModelIndex,
AnimateCameraSpin,
AnimateStateSnapshots,
AnimateAssemblyUnwind
AnimateAssemblyUnwind,
AnimateStateInterpolation
]
});

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>
*/
@@ -24,9 +24,6 @@ import { Visual } from './visual';
import { CustomProperty } from '../mol-model-props/common/custom-property';
import { Clipping } from '../mol-theme/clipping';
// export interface RepresentationProps {
// visuals?: string[]
// }
export type RepresentationProps = { [k: string]: any }
export interface RepresentationContext {
@@ -54,6 +51,8 @@ export interface RepresentationProvider<D = any, P extends PD.Params = any, S ex
attach: (ctx: CustomProperty.Context, data: D) => Promise<void>,
detach: (data: D) => void
}
readonly getData?: (data: D, props: PD.Values<P>) => D
readonly mustRecreate?: (oldProps: PD.Values<P>, newProps: PD.Values<P>) => boolean
}
export namespace RepresentationProvider {
@@ -66,7 +65,7 @@ export namespace RepresentationProvider {
export type AnyRepresentationProvider = RepresentationProvider<any, {}, Representation.State>
export const EmptyRepresentationProvider = {
const EmptyRepresentationProvider = {
label: '',
description: '',
factory: () => Representation.Empty,

View File

@@ -66,7 +66,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
}
function getLoci(pickingId?: PickingId) {
if (pickingId === undefined) return Structure.Loci(_structure);
if (pickingId === undefined) return Structure.Loci(_structure.target);
return visual ? visual.getLoci(pickingId) : EmptyLoci;
}

View File

@@ -24,6 +24,7 @@ const BallAndStickVisuals = {
export const BallAndStickParams = {
...ElementSphereParams,
traceOnly: PD.Boolean(false, { isHidden: true }), // not useful here
...IntraUnitBondCylinderParams,
...InterUnitBondCylinderParams,
unitKinds: getUnitKindsParam(['atomic']),
@@ -50,5 +51,11 @@ export const BallAndStickRepresentationProvider = StructureRepresentationProvide
defaultValues: PD.getDefaultValues(BallAndStickParams),
defaultColorTheme: { name: 'element-symbol' },
defaultSizeTheme: { name: 'physical' },
isApplicable: (structure: Structure) => structure.elementCount > 0
isApplicable: (structure: Structure) => structure.elementCount > 0,
getData: (structure: Structure, props: PD.Values<BallAndStickParams>) => {
return props.includeParent ? structure.asParent() : structure;
},
mustRecreate: (oldProps: PD.Values<BallAndStickParams>, newProps: PD.Values<BallAndStickParams>) => {
return oldProps.includeParent !== newProps.includeParent;
}
});

View File

@@ -25,6 +25,7 @@ export const EllipsoidParams = {
...EllipsoidMeshParams,
...IntraUnitBondCylinderParams,
...InterUnitBondCylinderParams,
adjustCylinderLength: PD.Boolean(false, { isHidden: true }), // not useful here
unitKinds: getUnitKindsParam(['atomic']),
sizeFactor: PD.Numeric(1, { min: 0.01, max: 10, step: 0.01 }),
sizeAspectRatio: PD.Numeric(0.1, { min: 0.01, max: 3, step: 0.01 }),
@@ -50,5 +51,11 @@ export const EllipsoidRepresentationProvider = StructureRepresentationProvider({
defaultValues: PD.getDefaultValues(EllipsoidParams),
defaultColorTheme: { name: 'element-symbol' },
defaultSizeTheme: { name: 'uniform' },
isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => AtomSiteAnisotrop.Provider.isApplicable(m))
isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => AtomSiteAnisotrop.Provider.isApplicable(m)),
getData: (structure: Structure, props: PD.Values<EllipsoidParams>) => {
return props.includeParent ? structure.asParent() : structure;
},
mustRecreate: (oldProps: PD.Values<EllipsoidParams>, newProps: PD.Values<EllipsoidParams>) => {
return oldProps.includeParent !== newProps.includeParent;
}
});

View File

@@ -46,5 +46,11 @@ export const LineRepresentationProvider = StructureRepresentationProvider({
defaultValues: PD.getDefaultValues(LineParams),
defaultColorTheme: { name: 'element-symbol' },
defaultSizeTheme: { name: 'uniform' },
isApplicable: (structure: Structure) => structure.elementCount > 0
isApplicable: (structure: Structure) => structure.elementCount > 0,
getData: (structure: Structure, props: PD.Values<LineParams>) => {
return props.includeParent ? structure.asParent() : structure;
},
mustRecreate: (oldProps: PD.Values<LineParams>, newProps: PD.Values<LineParams>) => {
return oldProps.includeParent !== newProps.includeParent;
}
});

View File

@@ -180,7 +180,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
}
function getLoci(pickingId?: PickingId) {
if (pickingId === undefined) return Structure.Loci(_structure);
if (pickingId === undefined) return Structure.Loci(_structure.target);
let loci: Loci = EmptyLoci;
visuals.forEach(({ visual }) => {
const _loci = visual.getLoci(pickingId);

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>
*/
@@ -19,6 +19,7 @@ import { BondCylinderParams, BondIterator, getInterBondLoci, eachInterBond, make
import { Sphere3D } from '../../../mol-math/geometry';
import { Cylinders } from '../../../mol-geo/geometry/cylinders/cylinders';
import { WebGLContext } from '../../../mol-gl/webgl/context';
import { SortedArray } from '../../../mol-data/int/sorted-array';
const tmpRefPosBondIt = new Bond.ElementBondIterator();
function setRefPosition(pos: Vec3, structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex) {
@@ -39,10 +40,33 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
const bonds = structure.interUnitBonds;
const { edgeCount, edges } = bonds;
const { sizeFactor, sizeAspectRatio } = props;
const { sizeFactor, sizeAspectRatio, adjustCylinderLength } = props;
const delta = Vec3();
let stub: undefined | ((edgeIndex: number) => boolean);
if (props.includeParent) {
const { child } = structure;
if (!child) throw new Error('expected child to exist');
stub = (edgeIndex: number) => {
const b = edges[edgeIndex];
const childUnitA = child.unitMap.get(b.unitA);
const childUnitB = child.unitMap.get(b.unitB);
const unitA = structure.unitMap.get(b.unitA);
const eA = unitA.elements[b.indexA];
const unitB = structure.unitMap.get(b.unitB);
const eB = unitB.elements[b.indexB];
return (
childUnitA && SortedArray.has(childUnitA.elements, eA) &&
(!childUnitB || !SortedArray.has(childUnitB.elements, eB))
);
};
}
const radius = (edgeIndex: number) => {
const b = edges[edgeIndex];
locB.aUnit = structure.unitMap.get(b.unitA);
@@ -92,19 +116,20 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
const uA = structure.unitMap.get(b.unitA);
const uB = structure.unitMap.get(b.unitB);
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
const r = Math.min(rA, rB) * sizeAspectRatio;
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
uA.conformation.position(uA.elements[b.indexA], posA);
uB.conformation.position(uB.elements[b.indexB], posB);
if (oA <= 0.01 && oB <= 0.01) return;
if (adjustCylinderLength) {
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
const r = Math.min(rA, rB) * sizeAspectRatio;
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
if (oA <= 0.01 && oB <= 0.01) return;
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
Vec3.scaleAndAdd(posA, posA, delta, oA);
Vec3.scaleAndAdd(posB, posB, delta, -oB);
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
Vec3.scaleAndAdd(posA, posA, delta, oA);
Vec3.scaleAndAdd(posB, posB, delta, -oB);
}
},
style: (edgeIndex: number) => {
const o = edges[edgeIndex].props.order;
@@ -123,7 +148,8 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
radius: (edgeIndex: number) => {
return radius(edgeIndex) * sizeAspectRatio;
},
ignore: makeInterBondIgnoreTest(structure, props)
ignore: makeInterBondIgnoreTest(structure, props),
stub
};
}
@@ -133,7 +159,8 @@ function createInterUnitBondCylinderImpostors(ctx: VisualContext, structure: Str
const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
const m = createLinkCylinderImpostors(ctx, builderProps, props, cylinders);
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
const { child } = structure;
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
m.setBoundingSphere(sphere);
return m;
@@ -145,7 +172,8 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
const { child } = structure;
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
m.setBoundingSphere(sphere);
return m;
@@ -158,6 +186,7 @@ export const InterUnitBondCylinderParams = {
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0, max: 3, step: 0.01 }),
tryUseImpostor: PD.Boolean(true),
includeParent: PD.Boolean(false),
};
export type InterUnitBondCylinderParams = typeof InterUnitBondCylinderParams
@@ -183,8 +212,10 @@ export function InterUnitBondCylinderImpostorVisual(materialId: number): Complex
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.stubCap !== currentProps.stubCap ||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
);
},
mustRecreate: (structure: Structure, props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {
@@ -212,8 +243,10 @@ export function InterUnitBondCylinderMeshVisual(materialId: number): ComplexVisu
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.stubCap !== currentProps.stubCap ||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
);
},
mustRecreate: (structure: Structure, props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {

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>
*/
@@ -97,7 +97,8 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
const l = createLinkLines(ctx, builderProps, props, lines);
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
const { child } = structure;
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
l.setBoundingSphere(sphere);
return l;
@@ -106,6 +107,7 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
export const InterUnitBondLineParams = {
...ComplexLinesParams,
...BondLineParams,
includeParent: PD.Boolean(false),
};
export type InterUnitBondLineParams = typeof InterUnitBondLineParams

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>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -21,23 +21,39 @@ import { Sphere3D } from '../../../mol-math/geometry';
import { IntAdjacencyGraph } from '../../../mol-math/graph';
import { WebGLContext } from '../../../mol-gl/webgl/context';
import { Cylinders } from '../../../mol-geo/geometry/cylinders/cylinders';
import { SortedArray } from '../../../mol-data/int';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const isBondType = BondType.is;
function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondCylinderParams>) {
const locE = StructureElement.Location.create(structure, unit);
const locB = Bond.Location(structure, unit, undefined, structure, unit, undefined);
const elements = unit.elements;
const bonds = unit.bonds;
const { edgeCount, a, b, edgeProps, offset } = bonds;
const { order: _order, flags: _flags } = edgeProps;
const { sizeFactor, sizeAspectRatio } = props;
const { sizeFactor, sizeAspectRatio, adjustCylinderLength } = props;
const vRef = Vec3(), delta = Vec3();
const pos = unit.conformation.invariantPosition;
let stub: undefined | ((edgeIndex: number) => boolean);
const locE = StructureElement.Location.create(structure, unit);
const locB = Bond.Location(structure, unit, undefined, structure, unit, undefined);
if (props.includeParent) {
const { child } = structure;
if (!child) throw new Error('expected child to exist');
const childUnit = child.unitMap.get(unit.id);
if (!childUnit) throw new Error('expected childUnit to exist');
stub = (edgeIndex: number) => {
const eA = elements[a[edgeIndex]];
const eB = elements[b[edgeIndex]];
return SortedArray.has(childUnit.elements, eA) && !SortedArray.has(childUnit.elements, eB);
};
}
const radius = (edgeIndex: number) => {
locB.aIndex = a[edgeIndex];
locB.bIndex = b[edgeIndex];
@@ -74,19 +90,20 @@ function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Stru
return null;
},
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
const r = Math.min(rA, rB) * sizeAspectRatio;
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
pos(elements[a[edgeIndex]], posA);
pos(elements[b[edgeIndex]], posB);
if (oA <= 0.01 && oB <= 0.01) return;
if (adjustCylinderLength) {
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
const r = Math.min(rA, rB) * sizeAspectRatio;
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
if (oA <= 0.01 && oB <= 0.01) return;
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
Vec3.scaleAndAdd(posA, posA, delta, oA);
Vec3.scaleAndAdd(posB, posB, delta, -oB);
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
Vec3.scaleAndAdd(posA, posA, delta, oA);
Vec3.scaleAndAdd(posB, posB, delta, -oB);
}
},
style: (edgeIndex: number) => {
const o = _order[edgeIndex];
@@ -105,7 +122,8 @@ function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Stru
radius: (edgeIndex: number) => {
return radius(edgeIndex) * sizeAspectRatio;
},
ignore: makeIntraBondIgnoreTest(unit, props)
ignore: makeIntraBondIgnoreTest(structure, unit, props),
stub
};
}
@@ -113,10 +131,14 @@ function createIntraUnitBondCylinderImpostors(ctx: VisualContext, unit: Unit, st
if (!Unit.isAtomic(unit)) return Cylinders.createEmpty(cylinders);
if (!unit.bonds.edgeCount) return Cylinders.createEmpty(cylinders);
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Cylinders.createEmpty(cylinders);
const builderProps = getIntraUnitBondCylinderBuilderProps(unit, structure, theme, props);
const c = createLinkCylinderImpostors(ctx, builderProps, props, cylinders);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * props.sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * props.sizeFactor);
c.setBoundingSphere(sphere);
return c;
@@ -126,10 +148,14 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
if (!unit.bonds.edgeCount) return Mesh.createEmpty(mesh);
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Mesh.createEmpty(mesh);
const builderProps = getIntraUnitBondCylinderBuilderProps(unit, structure, theme, props);
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * props.sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * props.sizeFactor);
m.setBoundingSphere(sphere);
return m;
@@ -142,6 +168,7 @@ export const IntraUnitBondCylinderParams = {
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0, max: 3, step: 0.01 }),
tryUseImpostor: PD.Boolean(true),
includeParent: PD.Boolean(false),
};
export type IntraUnitBondCylinderParams = typeof IntraUnitBondCylinderParams
@@ -167,8 +194,10 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.stubCap !== currentProps.stubCap ||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
);
const newUnit = newStructureGroup.group.units[0];
@@ -207,8 +236,10 @@ export function IntraUnitBondCylinderMeshVisual(materialId: number): UnitsVisual
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.stubCap !== currentProps.stubCap ||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
);
const newUnit = newStructureGroup.group.units[0];

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>
*/
@@ -25,6 +25,14 @@ const isBondType = BondType.is;
function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondLineParams>, lines?: Lines) {
if (!Unit.isAtomic(unit)) return Lines.createEmpty(lines);
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Lines.createEmpty(lines);
if (props.includeParent) {
if (!child) throw new Error('expected child to exist');
}
const location = StructureElement.Location.create(structure, unit);
const elements = unit.elements;
@@ -82,12 +90,12 @@ function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Str
const sizeB = theme.size.size(location);
return Math.min(sizeA, sizeB) * sizeFactor;
},
ignore: makeIntraBondIgnoreTest(unit, props)
ignore: makeIntraBondIgnoreTest(structure, unit, props)
};
const l = createLinkLines(ctx, builderProps, props, lines);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * sizeFactor);
l.setBoundingSphere(sphere);
return l;
@@ -96,6 +104,7 @@ function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Str
export const IntraUnitBondLineParams = {
...UnitsLinesParams,
...BondLineParams,
includeParent: PD.Boolean(false),
};
export type IntraUnitBondLineParams = typeof IntraUnitBondLineParams

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>
*/
@@ -29,17 +29,20 @@ export type ElementPointParams = typeof ElementPointParams
export function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ElementPointParams>, points: Points) {
// TODO sizeFactor
const { child } = structure;
if (child && !child.unitMap.get(unit.id)) return Points.createEmpty(points);
const elements = unit.elements;
const n = elements.length;
const builder = PointsBuilder.create(n, n / 10, points);
const p = Vec3();
const pos = unit.conformation.invariantPosition;
const ignore = makeElementIgnoreTest(unit, props);
const ignore = makeElementIgnoreTest(structure, unit, props);
if (ignore) {
for (let i = 0; i < n; ++i) {
if (ignore(unit, elements[i])) continue;
if (ignore(elements[i])) continue;
pos(elements[i], p);
builder.add(p[0], p[1], p[2], i);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 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>
*/
@@ -22,6 +22,7 @@ import { equalEps } from '../../../mol-math/linear-algebra/3d/common';
import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere';
import { Sphere3D } from '../../../mol-math/geometry';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
import { SortedArray } from '../../../mol-data/int/sorted-array';
export const EllipsoidMeshParams = {
...UnitsMeshParams,
@@ -57,7 +58,11 @@ export interface EllipsoidMeshProps {
}
export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: EllipsoidMeshProps, mesh?: Mesh): Mesh {
const { detail, sizeFactor } = props;
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Mesh.createEmpty(mesh);
const { detail, sizeFactor, ignoreHydrogens } = props;
const { elements, model } = unit;
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
@@ -84,7 +89,8 @@ export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: S
const ei = elements[i];
const ai = elementToAnsiotrop[ei];
if (ai === -1) continue;
if (props.ignoreHydrogens && isH(atomicNumber, ei)) continue;
if (((!!childUnit && !SortedArray.has(childUnit.elements, ei))) ||
(ignoreHydrogens && isH(atomicNumber, ei))) continue;
l.element = ei;
pos(ei, v);
@@ -111,7 +117,7 @@ export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: S
const m = MeshBuilder.getMesh(builderState);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
const sphere = Sphere3D.expand(Sphere3D(), (childUnit || unit).boundary.sphere, 1 * sizeFactor);
m.setBoundingSphere(sphere);
return m;

View File

@@ -182,7 +182,7 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit,
const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
const buffer = textureMesh?.doubleBuffer.get();
const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, buffer?.vertex, buffer?.group, buffer?.normal);
const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, buffer?.vertex, buffer?.group, buffer?.normal);
const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh);
@@ -241,7 +241,7 @@ async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, str
const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
const buffer = textureMesh?.doubleBuffer.get();
const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, buffer?.vertex, buffer?.group, buffer?.normal);
const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, buffer?.vertex, buffer?.group, buffer?.normal);
const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh);

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>
*/
@@ -13,7 +13,7 @@ import { LinkCylinderParams, LinkLineParams } from './link';
import { ObjectKeys } from '../../../../mol-util/type-helpers';
import { PickingId } from '../../../../mol-geo/geometry/picking';
import { EmptyLoci, Loci } from '../../../../mol-model/loci';
import { Interval, OrderedSet } from '../../../../mol-data/int';
import { Interval, OrderedSet, SortedArray } from '../../../../mol-data/int';
import { isH, isHydrogen } from './common';
export const BondParams = {
@@ -26,7 +26,8 @@ export type BondProps = typeof DefaultBondProps
export const BondCylinderParams = {
...LinkCylinderParams,
...BondParams
...BondParams,
adjustCylinderLength: PD.Boolean(true, { description: 'Shorten cylinders to reduce overlap with spheres.' })
};
export const DefaultBondCylinderProps = PD.getDefaultValues(BondCylinderParams);
export type BondCylinderProps = typeof DefaultBondCylinderProps
@@ -42,7 +43,7 @@ export function ignoreBondType(include: BondType.Flag, exclude: BondType.Flag, f
return !BondType.is(include, f) || BondType.is(exclude, f);
}
export function makeIntraBondIgnoreTest(unit: Unit.Atomic, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
export function makeIntraBondIgnoreTest(structure: Structure, unit: Unit.Atomic, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
const elements = unit.elements;
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
const bonds = unit.bonds;
@@ -53,16 +54,21 @@ export function makeIntraBondIgnoreTest(unit: Unit.Atomic, props: BondProps): un
const include = BondType.fromNames(includeTypes);
const exclude = BondType.fromNames(excludeTypes);
const allBondTypes = BondType.isAll(include) && BondType.Flag.None === exclude;
if (!allBondTypes && ignoreHydrogens) {
return (edgeIndex: number) => isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]) || ignoreBondType(include, exclude, _flags[edgeIndex]);
} else if (!allBondTypes) {
return (edgeIndex: number) => ignoreBondType(include, exclude, _flags[edgeIndex]);
} else if (ignoreHydrogens) {
return (edgeIndex: number) => isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]);
}
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) throw new Error('expected childUnit to exist if child exists');
if (allBondTypes && !ignoreHydrogens && !child) return;
return (edgeIndex: number) => {
return (
(!!childUnit && !SortedArray.has(childUnit.elements, elements[a[edgeIndex]])) ||
(ignoreHydrogens && (isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]))) ||
(!allBondTypes && ignoreBondType(include, exclude, _flags[edgeIndex]))
);
};
}
export function makeInterBondIgnoreTest(structure: Structure, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
@@ -73,34 +79,45 @@ export function makeInterBondIgnoreTest(structure: Structure, props: BondProps):
const include = BondType.fromNames(includeTypes);
const exclude = BondType.fromNames(excludeTypes);
const allBondTypes = BondType.isAll(include) && BondType.Flag.None === exclude;
const ignoreHydrogen = (edgeIndex: number) => {
const b = edges[edgeIndex];
const uA = structure.unitMap.get(b.unitA);
const uB = structure.unitMap.get(b.unitB);
return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB]);
};
const { child } = structure;
if (!allBondTypes && ignoreHydrogens) {
return (edgeIndex: number) => ignoreHydrogen(edgeIndex) || ignoreBondType(include, exclude, edges[edgeIndex].props.flag);
} else if (!allBondTypes) {
return (edgeIndex: number) => ignoreBondType(include, exclude, edges[edgeIndex].props.flag);
} else if (ignoreHydrogens) {
return (edgeIndex: number) => ignoreHydrogen(edgeIndex);
}
if (allBondTypes && !ignoreHydrogens && !child) return;
return (edgeIndex: number) => {
if (child) {
const b = edges[edgeIndex];
const childUnitA = child.unitMap.get(b.unitA);
if (!childUnitA) return true;
const unitA = structure.unitMap.get(b.unitA);
const eA = unitA.elements[b.indexA];
if (!SortedArray.has(childUnitA.elements, eA)) return true;
}
if (ignoreHydrogens) {
const b = edges[edgeIndex];
const uA = structure.unitMap.get(b.unitA);
const uB = structure.unitMap.get(b.unitB);
if(isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB])) return true;
}
if (!allBondTypes) {
if (ignoreBondType(include, exclude, edges[edgeIndex].props.flag)) return true;
}
return false;
};
}
export namespace BondIterator {
export function fromGroup(structureGroup: StructureGroup): LocationIterator {
const { group, structure } = structureGroup;
const unit = group.units[0];
const unit = group.units[0] as Unit.Atomic;
const groupCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0;
const instanceCount = group.units.length;
const location = Bond.Location();
location.aStructure = structure;
location.bStructure = structure;
const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
const getLocation = (groupIndex: number, instanceIndex: number) => {
const unit = group.units[instanceIndex] as Unit.Atomic;
location.aUnit = unit;
@@ -115,9 +132,7 @@ export namespace BondIterator {
export function fromStructure(structure: Structure): LocationIterator {
const groupCount = structure.interUnitBonds.edgeCount;
const instanceCount = 1;
const location = Bond.Location();
location.aStructure = structure;
location.bStructure = structure;
const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
const getLocation = (groupIndex: number) => {
const bond = structure.interUnitBonds.edges[groupIndex];
location.aUnit = structure.unitMap.get(bond.unitA);
@@ -138,15 +153,12 @@ export function getIntraBondLoci(pickingId: PickingId, structureGroup: Structure
const { structure, group } = structureGroup;
const unit = group.units[instanceId];
if (Unit.isAtomic(unit)) {
return Bond.Loci(structure, [
Bond.Location(
structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex,
structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex
),
Bond.Location(
structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex,
structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex
)
const { target } = structure;
const iA = unit.bonds.a[groupId];
const iB = unit.bonds.b[groupId];
return Bond.Loci(target, [
Bond.Location(target, unit, iA, target, unit, iB),
Bond.Location(target, unit, iB, target, unit, iA)
]);
}
}
@@ -199,12 +211,13 @@ export function eachIntraBond(loci: Loci, structureGroup: StructureGroup, apply:
export function getInterBondLoci(pickingId: PickingId, structure: Structure, id: number) {
const { objectId, groupId } = pickingId;
if (id === objectId) {
const { target } = structure;
const b = structure.interUnitBonds.edges[groupId];
const uA = structure.unitMap.get(b.unitA);
const uB = structure.unitMap.get(b.unitB);
return Bond.Loci(structure, [
Bond.Location(structure, uA, b.indexA, structure, uB, b.indexB),
Bond.Location(structure, uB, b.indexB, structure, uA, b.indexA)
return Bond.Loci(target, [
Bond.Location(target, uA, b.indexA, target, uB, b.indexB),
Bond.Location(target, uB, b.indexB, target, uA, b.indexA)
]);
}
return EmptyLoci;

View File

@@ -8,7 +8,7 @@
import { Vec3 } from '../../../../mol-math/linear-algebra';
import { Unit, StructureElement, Structure, ElementIndex } from '../../../../mol-model/structure';
import { Loci, EmptyLoci } from '../../../../mol-model/loci';
import { Interval, OrderedSet } from '../../../../mol-data/int';
import { Interval, OrderedSet, SortedArray } from '../../../../mol-data/int';
import { Mesh } from '../../../../mol-geo/geometry/mesh/mesh';
import { sphereVertexCount } from '../../../../mol-geo/primitive/sphere';
import { MeshBuilder } from '../../../../mol-geo/geometry/mesh/mesh-builder';
@@ -36,22 +36,32 @@ export type ElementSphereMeshProps = {
sizeFactor: number,
} & ElementProps
export function makeElementIgnoreTest(unit: Unit, props: ElementProps): undefined | ((unit: Unit, i: ElementIndex) => boolean) {
export function makeElementIgnoreTest(structure: Structure, unit: Unit, props: ElementProps): undefined | ((i: ElementIndex) => boolean) {
const { ignoreHydrogens, traceOnly } = props;
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
const isCoarse = Unit.isCoarse(unit);
if (!isCoarse && ignoreHydrogens && traceOnly) {
return (unit: Unit, element: ElementIndex) => isH(atomicNumber, element) && !isTrace(unit, element);
} else if (!isCoarse && ignoreHydrogens) {
return (unit: Unit, element: ElementIndex) => isH(atomicNumber, element);
} else if (!isCoarse && traceOnly) {
return (unit: Unit, element: ElementIndex) => !isTrace(unit, element);
}
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) throw new Error('expected childUnit to exist if child exists');
if (!child && ((!ignoreHydrogens && !traceOnly) || traceOnly)) return;
return (element: ElementIndex) => {
return (
(!!childUnit && !SortedArray.has(childUnit.elements, element)) ||
(!isCoarse && ignoreHydrogens && isH(atomicNumber, element)) ||
(traceOnly && !isTrace(unit, element))
);
};
}
export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh): Mesh {
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Mesh.createEmpty(mesh);
const { detail, sizeFactor } = props;
const { elements } = unit;
@@ -61,7 +71,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
const v = Vec3();
const pos = unit.conformation.invariantPosition;
const ignore = makeElementIgnoreTest(unit, props);
const ignore = makeElementIgnoreTest(structure, unit, props);
const l = StructureElement.Location.create(structure, unit);
const themeSize = theme.size.size;
const center = Vec3();
@@ -69,7 +79,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
let count = 0;
for (let i = 0; i < elementCount; i++) {
if (ignore && ignore(unit, elements[i])) continue;
if (ignore && ignore(elements[i])) continue;
l.element = elements[i];
pos(elements[i], v);
@@ -89,7 +99,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
if (mesh && Vec3.distance(center, mesh.boundingSphere.center) / mesh.boundingSphere.radius < 1.0) {
boundingSphere = Sphere3D.clone(mesh.boundingSphere);
} else {
boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, maxSize * sizeFactor + 0.05);
boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, maxSize * sizeFactor + 0.05);
}
const m = MeshBuilder.getMesh(builderState);
@@ -103,13 +113,17 @@ export type ElementSphereImpostorProps = {
} & ElementProps
export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres): Spheres {
const { child } = structure;
const childUnit = child?.unitMap.get(unit.id);
if (child && !childUnit) return Spheres.createEmpty(spheres);
const { elements } = unit;
const elementCount = elements.length;
const builder = SpheresBuilder.create(elementCount, elementCount / 2, spheres);
const v = Vec3();
const pos = unit.conformation.invariantPosition;
const ignore = makeElementIgnoreTest(unit, props);
const ignore = makeElementIgnoreTest(structure, unit, props);
const l = StructureElement.Location.create(structure, unit);
const themeSize = theme.size.size;
@@ -118,7 +132,7 @@ export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, stru
let count = 0;
for (let i = 0; i < elementCount; i++) {
if (ignore?.(unit, elements[i])) continue;
if (ignore?.(elements[i])) continue;
pos(elements[i], v);
builder.add(v[0], v[1], v[2], i);
@@ -136,7 +150,7 @@ export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, stru
if (spheres && Vec3.distance(center, spheres.boundingSphere.center) / spheres.boundingSphere.radius < 1.0) {
boundingSphere = Sphere3D.clone(spheres.boundingSphere);
} else {
boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, maxSize * props.sizeFactor + 0.05);
boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, maxSize * props.sizeFactor + 0.05);
}
const s = builder.getSpheres();
@@ -180,7 +194,7 @@ export function getElementLoci(pickingId: PickingId, structureGroup: StructureGr
const { structure, group } = structureGroup;
const unit = group.units[instanceId];
const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex);
return StructureElement.Loci(structure, [{ unit, indices }]);
return StructureElement.Loci(structure.target, [{ unit, indices }]);
}
return EmptyLoci;
}

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>
*/
@@ -24,6 +24,7 @@ export const LinkCylinderParams = {
dashCount: PD.Numeric(4, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.8, { min: 0, max: 2, step: 0.1 }),
dashCap: PD.Boolean(true),
stubCap: PD.Boolean(true),
radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }, BaseGeometry.CustomQualityParamInfo),
};
export const DefaultLinkCylinderProps = PD.getDefaultValues(LinkCylinderParams);
@@ -75,6 +76,7 @@ export interface LinkBuilderProps {
referencePosition?: (edgeIndex: number) => Vec3 | null
style?: (edgeIndex: number) => LinkStyle
ignore?: (edgeIndex: number) => boolean
stub?: (edgeIndex: number) => boolean
}
export const enum LinkStyle {
@@ -97,11 +99,11 @@ const v3dot = Vec3.dot;
* the half closer to the first vertex, i.e. vertex a.
*/
export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
const { linkCount, referencePosition, position, style, radius, ignore } = linkBuilder;
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
if (!linkCount) return Mesh.createEmpty(mesh);
const { linkScale, linkSpacing, radialSegments, linkCap, dashCount, dashScale, dashCap } = props;
const { linkScale, linkSpacing, radialSegments, linkCap, dashCount, dashScale, dashCap, stubCap } = props;
const vertexCountEstimate = radialSegments * 2 * linkCount * 2;
const builderState = MeshBuilder.createState(vertexCountEstimate, vertexCountEstimate / 4, mesh);
@@ -127,7 +129,8 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
const linkRadius = radius(edgeIndex);
const linkStyle = style ? style(edgeIndex) : LinkStyle.Solid;
const [topCap, bottomCap] = (v3dot(tmpV12, up) > 0) ? [false, linkCap] : [linkCap, false];
const linkStub = stubCap && (stub ? stub(edgeIndex) : false);
const [topCap, bottomCap] = (v3dot(tmpV12, up) > 0) ? [linkStub, linkCap] : [linkCap, linkStub];
builderState.currentGroup = edgeIndex;
if (linkStyle === LinkStyle.Solid) {
@@ -176,11 +179,11 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
* the half closer to the first vertex, i.e. vertex a.
*/
export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: LinkBuilderProps, props: LinkCylinderProps, cylinders?: Cylinders) {
const { linkCount, referencePosition, position, style, radius, ignore } = linkBuilder;
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
if (!linkCount) return Cylinders.createEmpty(cylinders);
const { linkScale, linkSpacing, linkCap, dashCount, dashScale, dashCap } = props;
const { linkScale, linkSpacing, linkCap, dashCount, dashScale, dashCap, stubCap } = props;
const cylindersCountEstimate = linkCount * 2;
const builder = CylindersBuilder.create(cylindersCountEstimate, cylindersCountEstimate / 4, cylinders);
@@ -200,10 +203,11 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
const linkRadius = radius(edgeIndex);
const linkStyle = style ? style(edgeIndex) : LinkStyle.Solid;
const linkStub = stubCap && (stub ? stub(edgeIndex) : false);
if (linkStyle === LinkStyle.Solid) {
v3scale(vb, v3add(vb, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, false, edgeIndex);
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
} else if (linkStyle === LinkStyle.Dashed) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
v3sub(vb, vb, tmpV12);
@@ -218,14 +222,14 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
v3setMagnitude(vShift, vShift, absOffset);
if (order === 3) builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], multiScale, linkCap, false, edgeIndex);
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vb[0] + vShift[0], vb[1] + vShift[1], vb[2] + vShift[2], multiScale, linkCap, false, edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vb[0] - vShift[0], vb[1] - vShift[1], vb[2] - vShift[2], multiScale, linkCap, false, edgeIndex);
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vb[0] + vShift[0], vb[1] + vShift[1], vb[2] + vShift[2], multiScale, linkCap, linkStub, edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vb[0] - vShift[0], vb[1] - vShift[1], vb[2] - vShift[2], multiScale, linkCap, linkStub, edgeIndex);
} else if (linkStyle === LinkStyle.Disk) {
v3scale(tmpV12, v3sub(tmpV12, vb, va), 0.475);
v3add(va, va, tmpV12);
v3sub(vb, vb, tmpV12);
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, false, edgeIndex);
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
}
}

View File

@@ -176,7 +176,7 @@ async function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Vol
const { texture, gridDimension, gridTexDim, gridTexScale, transform } = VolumeIsosurfaceTexture.get(volume, ctx.webgl);
const buffer = textureMesh?.doubleBuffer.get();
const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, false, buffer?.vertex, buffer?.group, buffer?.normal);
const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, value < 0, false, buffer?.vertex, buffer?.group, buffer?.normal);
const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, Volume.getBoundingSphere(volume), textureMesh);

View File

@@ -11,7 +11,7 @@ import { MolScriptSymbolTable as MolScript } from '../../language/symbol-table';
import { Type } from '../../language/type';
import { Types as StructureQueryTypes } from '../../language/symbol-table/structure-query';
import { MolScriptBuilder as B } from '../../language/builder';
import { getPositionalArgs, tryGetArg } from './macro';
import { getPositionalArgs, tryGetArg } from './script-macro';
export type MolScriptSymbol =
| { kind: 'alias', aliases: string[], symbol: MSymbol }

View File

@@ -35,6 +35,7 @@ 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';
export type LocationColor = (location: Location, isSecondary: boolean) => Color
@@ -44,6 +45,9 @@ interface ColorTheme<P extends PD.Params> {
readonly granularity: ColorType
readonly color: LocationColor
readonly props: Readonly<PD.Values<P>>
// if palette is defined, 24bit RGB color value normalized to interval [0, 1]
// is used as index to the colors
readonly palette?: Readonly<ColorTheme.Palette>
readonly contextHash?: number
readonly description?: string
readonly legend?: Readonly<ScaleLegend | TableLegend>
@@ -58,6 +62,13 @@ namespace ColorTheme {
Misc = 'Miscellaneous',
}
export interface Palette {
filter?: TextureFilter,
colors: Color[]
}
export const PaletteScale = (1 << 24) - 1;
export type Props = { [k: string]: any }
export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
export const EmptyFactory = () => Empty;

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>
*/
@@ -10,16 +10,16 @@ import { SyncRuntimeContext } from '../../mol-task/execution/synchronous';
describe('zip', () => {
it('roundtrip deflate/inflate', async () => {
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7]);
const deflated = deflate(data);
const deflated = await deflate(SyncRuntimeContext, data);
const inflated = await inflate(SyncRuntimeContext, deflated);
expect(inflated).toEqual(data);
});
it('roundtrip zip', async () => {
it('roundtrip zip/unzip', async () => {
const data = {
'test.foo': new Uint8Array([1, 2, 3, 4, 5, 6, 7])
};
const zipped = zip(data);
const zipped = await zip(SyncRuntimeContext, data);
const unzipped = await unzip(SyncRuntimeContext, zipped);
expect(unzipped).toEqual(data);
});

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,65 +7,54 @@
* MIT License, Copyright (c) 2018 Photopea
*/
import { RuntimeContext } from '../../mol-task';
import { NumberArray } from '../type-helpers';
import { _hufTree } from './huffman';
import { U, revCodes, makeCodes } from './util';
export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
const opts = [
/*
ush good_length; /* reduce lazy search above this match length
ush max_lazy; /* do not perform lazy search above this match length
ush nice_length; /* quit search above this match length
*/
/* good lazy nice chain */
/* 0 */ [ 0, 0, 0, 0, 0], /* store only */
/* 1 */ [ 4, 4, 8, 4, 0], /* max speed, no lazy matches */
/* 2 */ [ 4, 5, 16, 8, 0],
/* 3 */ [ 4, 6, 16, 16, 0],
/* 4 */ [ 4, 10, 16, 32, 0], /* lazy matches */
/* 5 */ [ 8, 16, 32, 32, 0],
/* 6 */ [ 8, 16, 128, 128, 0],
/* 7 */ [ 8, 32, 128, 256, 0],
/* 8 */ [32, 128, 258, 1024, 1],
/* 9 */ [32, 258, 258, 4096, 1] /* max compression */
];
const opt = opts[lvl];
let i = 0, pos = opos << 3, cvrd = 0;
const dlen = data.length;
if(lvl === 0) {
while(i < dlen) {
const len = Math.min(0xffff, dlen - i);
_putsE(out, pos, (i + len === dlen ? 1 : 0));
pos = _copyExact(data, i, len, out, pos + 8);
i += len;
}
return pos >>> 3;
}
function DeflateContext(data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
const { lits, strt, prev } = U;
let li = 0, lc = 0, bs = 0, ebits = 0, c = 0, nc = 0; // last_item, literal_count, block_start
if(dlen > 2) {
nc = _hash(data, 0);
strt[nc] = 0;
}
return {
data,
out,
opt: Opts[lvl],
i: 0,
pos: opos << 3,
cvrd: 0,
dlen: data.length,
// let nmch = 0
// let nmci = 0
li: 0,
lc: 0,
bs: 0,
ebits: 0,
c: 0,
nc: 0,
for(i = 0; i < dlen; i++) {
lits,
strt,
prev
};
}
type DeflateContext = ReturnType<typeof DeflateContext>
function deflateChunk(ctx: DeflateContext, count: number) {
const { data, dlen, out, opt } = ctx;
let { i, pos, cvrd, li, lc, bs, ebits, c, nc } = ctx;
const { lits, strt, prev } = U;
const end = Math.min(i + count, dlen);
for(; i < end; i++) {
c = nc;
//*
if(i + 1 < dlen - 2) {
nc = _hash(data, i + 1);
const ii = ((i + 1) & 0x7fff);
prev[ii] = strt[nc];
strt[nc] = ii;
} // */
}
if(cvrd <= i) {
if((li > 14000 || lc > 26697) && (dlen - i) > 100) {
if(cvrd < i) {
@@ -79,19 +68,12 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
}
let mch = 0;
// if(nmci==i) mch= nmch; else
if(i < dlen - 2) {
mch = _bestMatch(data, i, prev, c, Math.min(opt[2], dlen - i), opt[3]);
}
/*
if(mch!=0 && opt[4]==1 && (mch>>>16)<opt[1] && i+1<dlen-2) {
nmch = UZIP.F._bestMatch(data, i+1, prev, nc, opt[2], opt[3]); nmci=i+1;
//var mch2 = UZIP.F._bestMatch(data, i+2, prev, nnc); //nmci=i+1;
if((nmch>>>16)>(mch>>>16)) mch=0;
}//*/
// const len = mch>>>16, dst = mch & 0xffff; // if(i-dst<0) throw "e";
if(mch !== 0) {
const len = mch >>> 16, dst = mch & 0xffff; // if(i-dst<0) throw "e";
const len = mch >>> 16, dst = mch & 0xffff;
const lgi = _goodIndex(len, U.of0); U.lhst[257 + lgi]++;
const dgi = _goodIndex(dst, U.df0); U.dhst[ dgi]++; ebits += U.exb[lgi] + U.dxb[dgi];
lits[li] = (len << 23) | (i - cvrd); lits[li + 1] = (dst << 16) | (lgi << 8) | dgi; li += 2;
@@ -102,6 +84,69 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
lc++;
}
}
ctx.i = i;
ctx.pos = pos;
ctx.cvrd = cvrd;
ctx.li = li;
ctx.lc = lc;
ctx.bs = bs;
ctx.ebits = ebits;
ctx.c = c;
ctx.nc = nc;
}
/**
* - good_length: reduce lazy search above this match length;
* - max_lazy: do not perform lazy search above this match length;
* - nice_length: quit search above this match length;
*/
const Opts = [
/* good lazy nice chain */
/* 0 */ [ 0, 0, 0, 0, 0], /* store only */
/* 1 */ [ 4, 4, 8, 4, 0], /* max speed, no lazy matches */
/* 2 */ [ 4, 5, 16, 8, 0],
/* 3 */ [ 4, 6, 16, 16, 0],
/* 4 */ [ 4, 10, 16, 32, 0], /* lazy matches */
/* 5 */ [ 8, 16, 32, 32, 0],
/* 6 */ [ 8, 16, 128, 128, 0],
/* 7 */ [ 8, 32, 128, 256, 0],
/* 8 */ [32, 128, 258, 1024, 1],
/* 9 */ [32, 258, 258, 4096, 1] /* max compression */
] as const;
export async function _deflateRaw(runtime: RuntimeContext, data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
const ctx = DeflateContext(data, out, opos, lvl);
const { dlen } = ctx;
if(lvl === 0) {
let { i, pos } = ctx;
while(i < dlen) {
const len = Math.min(0xffff, dlen - i);
_putsE(out, pos, (i + len === dlen ? 1 : 0));
pos = _copyExact(data, i, len, out, pos + 8);
i += len;
}
return pos >>> 3;
}
if(dlen > 2) {
ctx.nc = _hash(data, 0);
ctx.strt[ctx.nc] = 0;
}
while (ctx.i < dlen) {
if (runtime.shouldUpdate) {
await runtime.update({ message: 'Deflating...', current: ctx.i, max: dlen });
}
deflateChunk(ctx, 1024 * 1024);
}
let { li, cvrd, pos } = ctx;
const { i, lits, bs, ebits } = ctx;
if(bs !== i || data.length === 0) {
if(cvrd < i) {
lits[li] = i - cvrd;
@@ -109,10 +154,6 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
cvrd = i;
}
pos = _writeBlock(1, lits, li, ebits, data, bs, i - bs, out, pos);
li = 0;
lc = 0;
li = lc = ebits = 0;
bs = i;
}
while((pos & 7) !== 0) pos++;
return pos >>> 3;

View File

@@ -13,7 +13,7 @@ import { writeUint, writeUshort, sizeUTF8, writeUTF8, readUshort, readUint, read
import { crc, adler } from './checksum';
import { _inflate } from './inflate';
import { _deflateRaw } from './deflate';
import { RuntimeContext } from '../../mol-task';
import { RuntimeContext, Task } from '../../mol-task';
export async function unzip(runtime: RuntimeContext, buf: ArrayBuffer, onlyNames = false) {
const out: { [k: string]: Uint8Array | { size: number, csize: number } } = Object.create(null);
@@ -174,12 +174,12 @@ export async function ungzip(runtime: RuntimeContext, file: Uint8Array, buf?: Ui
return inflated;
}
export function deflate(data: Uint8Array, opts?: { level: number }/* , buf, off*/) {
export async function deflate(runtime: RuntimeContext, data: Uint8Array, opts?: { level: number }/* , buf, off*/) {
if(opts === undefined) opts = { level: 6 };
let off = 0;
const buf = new Uint8Array(50 + Math.floor(data.length * 1.1));
buf[off] = 120; buf[off + 1] = 156; off += 2;
off = _deflateRaw(data, buf, off, opts.level);
off = await _deflateRaw(runtime, data, buf, off, opts.level);
const crcValue = adler(data, 0, data.length);
buf[off + 0] = ((crcValue >>> 24) & 255);
buf[off + 1] = ((crcValue >>> 16) & 255);
@@ -188,14 +188,18 @@ export function deflate(data: Uint8Array, opts?: { level: number }/* , buf, off*
return new Uint8Array(buf.buffer, 0, off + 4);
}
function deflateRaw(data: Uint8Array, opts?: { level: number }) {
async function deflateRaw(runtime: RuntimeContext, data: Uint8Array, opts?: { level: number }) {
if(opts === undefined) opts = { level: 6 };
const buf = new Uint8Array(50 + Math.floor(data.length * 1.1));
const off = _deflateRaw(data, buf, 0, opts.level);
const off = await _deflateRaw(runtime, data, buf, 0, opts.level);
return new Uint8Array(buf.buffer, 0, off);
}
export function zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
export function Zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
return Task.create('Zip', ctx => zip(ctx, obj, noCmpr));
}
export async function zip(runtime: RuntimeContext, obj: { [k: string]: Uint8Array }, noCmpr = false) {
let tot = 0;
const zpd: { [k: string]: { cpr: boolean, usize: number, crc: number, file: Uint8Array } } = {};
for(const p in obj) {
@@ -205,7 +209,7 @@ export function zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
cpr,
usize: buf.length,
crc: crcValue,
file: (cpr ? deflateRaw(buf) : buf)
file: (cpr ? await deflateRaw(runtime, buf) : buf)
};
}

View File

@@ -4,6 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Mat4 } from '../../../mol-math/linear-algebra';
import { ConsoleLogger } from '../../../mol-util/console-logger';
import { now } from '../../../mol-util/now';
import { PerformanceMonitor } from '../../../mol-util/performance-monitor';
@@ -18,6 +19,8 @@ export type Entry<Q extends QueryName = QueryName> = {
query: Q,
modelNums?: number[],
copyAllCategories?: boolean,
// column major 4x4 transformation matrix, provided as array of 16 float values
transform?: number[],
params?: QueryParams<Q>,
}
@@ -43,6 +46,7 @@ export async function runLocal(input: LocalInput) {
queryName: q.query,
queryParams: q.params || { },
modelNums: q.modelNums,
transform: q.transform as Mat4 ?? Mat4.identity(),
copyAllCategories: !!q.copyAllCategories
})),
writer: job.asTarGz

View File

@@ -73,7 +73,7 @@ async function init() {
console.timeEnd('gpu mc pyramid2');
console.time('gpu mc vert2');
createIsosurfaceBuffers(webgl, activeVoxelsTex2, densityTextureData2.texture, compacted2, densityTextureData2.gridDim, densityTextureData2.gridTexDim, densityTextureData2.transform, isoValue, true);
createIsosurfaceBuffers(webgl, activeVoxelsTex2, densityTextureData2.texture, compacted2, densityTextureData2.gridDim, densityTextureData2.gridTexDim, densityTextureData2.transform, isoValue, false, true);
webgl.waitForGpuCommandsCompleteSync();
console.timeEnd('gpu mc vert2');
console.timeEnd('gpu mc2');
@@ -96,7 +96,7 @@ async function init() {
console.timeEnd('gpu mc pyramid');
console.time('gpu mc vert');
const gv = createIsosurfaceBuffers(webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoValue, true);
const gv = createIsosurfaceBuffers(webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoValue, false, true);
webgl.waitForGpuCommandsCompleteSync();
console.timeEnd('gpu mc vert');
console.timeEnd('gpu mc');

View File

@@ -14,7 +14,7 @@
"moduleResolution": "node",
"importHelpers": true,
"noEmitHelpers": true,
"jsx": "react",
"jsx": "react-jsx",
"lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
"rootDir": "src",
"outDir": "lib/commonjs"

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