mirror of
https://github.com/molstar/molstar.git
synced 2026-06-06 14:44:22 +08:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e843c20cc | ||
|
|
ecaf19c5fb | ||
|
|
f024aeef2c | ||
|
|
9d9985f117 | ||
|
|
a0f7349ef6 | ||
|
|
01407427d2 | ||
|
|
3dee03d9b6 | ||
|
|
737f6593be | ||
|
|
068e10dd40 | ||
|
|
c1ba5248b0 | ||
|
|
4af0f22ac0 | ||
|
|
25a67e1176 | ||
|
|
a8fcd501d6 | ||
|
|
573ee92889 | ||
|
|
2558d6fada | ||
|
|
2cf3f8d62b | ||
|
|
589d89b0d5 | ||
|
|
7cc7b77460 | ||
|
|
e8a9995bef | ||
|
|
74ff283e00 | ||
|
|
1ecb960b82 | ||
|
|
387d59f97b | ||
|
|
d993082f24 | ||
|
|
5eaa73d56d | ||
|
|
b9428fd3cd | ||
|
|
97d180b79d | ||
|
|
25bd915ea5 | ||
|
|
f8fdffdc44 | ||
|
|
d11aa6ea77 | ||
|
|
fc3c7997ea | ||
|
|
b3aecf8de4 | ||
|
|
f3581e62ef | ||
|
|
88e7fe508f | ||
|
|
98049ed02d | ||
|
|
194092ed67 | ||
|
|
e96157c890 | ||
|
|
a028c1ef42 | ||
|
|
ad2b5e687d | ||
|
|
8ba19f0be4 | ||
|
|
bccc68f6df | ||
|
|
026a05d03d | ||
|
|
2b4741c8ee | ||
|
|
7960ee06d4 | ||
|
|
f73f5af131 | ||
|
|
3123110aa4 | ||
|
|
154063638d | ||
|
|
a720b98365 | ||
|
|
d4a2937e0b | ||
|
|
b0ca7ffbb7 | ||
|
|
c42b738abe | ||
|
|
ab0d0fec53 | ||
|
|
8d96131962 | ||
|
|
95bbcd8b24 | ||
|
|
a21f5c2c23 | ||
|
|
94b7b1281c | ||
|
|
16dba586df | ||
|
|
72b761f959 | ||
|
|
943d81cbf9 | ||
|
|
ea612c3acb | ||
|
|
a1308645e5 | ||
|
|
794b705184 | ||
|
|
66264abe50 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -5,7 +5,33 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v5.3.0] - 2025-11-5
|
||||
## [v5.4.2] - 2025-12-07
|
||||
- Fix postprocessing issues with SSAO and outlines for large structures (#1387)
|
||||
- Reduce automatic quality on standalone HMD devices
|
||||
|
||||
## [v5.4.1] - 2025-11-16
|
||||
- Fix ugly camera clipping in snapshot transitions
|
||||
- Add viewport button to toggle illumination mode
|
||||
- Fix bounding sphere computation for 3D text
|
||||
- Structure bounding sphere includes atom VDW radii / coarse sphere radii
|
||||
- Relax camera limits to allow focusing any selection with >1 atom
|
||||
- MolViewSpec
|
||||
- Fix `appendSnapshots` when loading MVSX
|
||||
- Fix all-selector color not applying on substructure
|
||||
- Fix primitives in root not being transformed with reference structure
|
||||
- Color themes do not prefer smoothing (improves performance in animations)
|
||||
- Allow canvas background interpolation
|
||||
- Fix `direct-volume` not drawn in illumination mode
|
||||
- Fix default trackball animated spin speed
|
||||
- Use `PluginCommands` to set canvas3d props in camera behavior
|
||||
- Volume improvements
|
||||
- Add `Volume.periodicity`
|
||||
- Wrap isosurfaces for periodic volumes
|
||||
- Fix dimensions for slices
|
||||
- Add support for Input Method Editor (IME) to text params input
|
||||
- Update `guessCifVariant` to detect density files not generated by the VolumeServer
|
||||
|
||||
## [v5.3.0] - 2025-11-05
|
||||
- Update loading message in MVS Stories Viewer
|
||||
- Add `Canvas3D.setAttribs`
|
||||
- Fix `normalizeWheel` "spin" calculation fallback
|
||||
|
||||
6393
package-lock.json
generated
6393
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "5.3.0",
|
||||
"version": "5.4.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -131,51 +131,51 @@
|
||||
"@types/gl": "^6.0.5",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
"@types/react": "^18.3.24",
|
||||
"@types/react": "^18.3.26",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@types/webxr": "^0.5.23",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.1",
|
||||
"@typescript-eslint/parser": "^8.44.1",
|
||||
"@types/webxr": "^0.5.24",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^9.2.1",
|
||||
"cpx2": "^8.0.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"esbuild": "^0.25.10",
|
||||
"esbuild": "^0.27.1",
|
||||
"esbuild-jest-transform": "^2.0.1",
|
||||
"esbuild-sass-plugin": "^3.3.1",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint": "^9.39.1",
|
||||
"fs-extra": "^11.3.2",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^30.2.0",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"sass": "^1.93.2",
|
||||
"simple-git": "^3.28.0",
|
||||
"sass": "^1.94.2",
|
||||
"simple-git": "^3.30.0",
|
||||
"tsc-alias": "^1.8.16",
|
||||
"typescript": "^5.9.2"
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.17",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/compression": "1.8.1",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/node": "^20.19.17",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^20.19.25",
|
||||
"@types/node-fetch": "^2.6.13",
|
||||
"@types/swagger-ui-dist": "3.30.6",
|
||||
"argparse": "^2.0.1",
|
||||
"compression": "^1.8.1",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"express": "^5.2.1",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immutable": "^5.1.3",
|
||||
"immutable": "^5.1.4",
|
||||
"io-ts": "^2.2.22",
|
||||
"mutative": "^1.3.0",
|
||||
"node-fetch": "^2.7.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"rxjs": "^7.8.2",
|
||||
"swagger-ui-dist": "^5.29.0",
|
||||
"swagger-ui-dist": "^5.30.3",
|
||||
"tslib": "^2.8.1",
|
||||
"util.promisify": "^1.1.3"
|
||||
},
|
||||
|
||||
@@ -531,7 +531,7 @@ export class Viewer {
|
||||
} else if (format === 'mvsx') {
|
||||
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
|
||||
await this.plugin.runTask(Task.create('Load MVSX file', async ctx => {
|
||||
const parsed = await loadMVSX(this.plugin, ctx, data);
|
||||
const parsed = await loadMVSX(this.plugin, ctx, data, { doNotClearAssets: options?.appendSnapshots });
|
||||
await loadMVS(this.plugin, parsed.mvsData, { sanityChecks: true, sourceUrl: parsed.sourceUrl, ...options });
|
||||
}));
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2025 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>
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
@@ -38,7 +39,7 @@ function print(volume: Volume) {
|
||||
}
|
||||
|
||||
async function doMesh(volume: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, -1, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) })).run();
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, -1, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5), wrap: 'auto' })).run();
|
||||
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
|
||||
|
||||
// Export the mesh in OBJ format.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2023-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -33,7 +33,7 @@ import { MVSTreeSchema } from './tree/mvs/mvs-tree';
|
||||
|
||||
|
||||
const DefaultFocusOptions = {
|
||||
minRadius: 5,
|
||||
minRadius: 1,
|
||||
extraRadius: 0,
|
||||
};
|
||||
const DefaultCanvasBackgroundColor = ColorNames.white;
|
||||
|
||||
@@ -117,7 +117,7 @@ export function MVSAnnotationColorTheme(ctx: ThemeDataContext, props: MVSAnnotat
|
||||
return {
|
||||
factory: MVSAnnotationColorTheme,
|
||||
granularity: 'groupInstance',
|
||||
preferSmoothing: true,
|
||||
preferSmoothing: false,
|
||||
color: color,
|
||||
props: props,
|
||||
description: 'Assigns colors based on custom MolViewSpec annotation data.',
|
||||
|
||||
@@ -112,11 +112,18 @@ export const MVSXFormatProvider: DataFormatProvider<{}, StateObjectRef<Mvs>, any
|
||||
* add all contained files to `plugin`'s asset manager,
|
||||
* and parse the main file in the archive as MVSJ.
|
||||
* Return parsed MVS data and `sourceUrl` for resolution of relative URIs. */
|
||||
export async function loadMVSX(plugin: PluginContext, runtimeCtx: RuntimeContext, data: Uint8Array<ArrayBuffer>, mainFilePath: string = 'index.mvsj'): Promise<{ mvsData: MVSData, sourceUrl: string }> {
|
||||
export async function loadMVSX(plugin: PluginContext, runtimeCtx: RuntimeContext, data: Uint8Array<ArrayBuffer>, mainFilePathOrOptions?: string | { mainFilePath?: string, doNotClearAssets?: boolean }): Promise<{ mvsData: MVSData, sourceUrl: string }> {
|
||||
// TODO: on next major version, streamline mainFilePathOrOptions
|
||||
if (typeof mainFilePathOrOptions === 'string') {
|
||||
mainFilePathOrOptions = { mainFilePath: mainFilePathOrOptions };
|
||||
}
|
||||
const mainFilePath = mainFilePathOrOptions?.mainFilePath ?? 'index.mvsj';
|
||||
const doNotClearAssets = mainFilePathOrOptions?.doNotClearAssets ?? false;
|
||||
|
||||
// Ensure at most one generation of MVSX file assets exists in the asset manager.
|
||||
// Hopefully, this is a reasonable compromise to ensure MVSX files work in multi-snapshot
|
||||
// states.
|
||||
clearMVSXFileAssets(plugin);
|
||||
if (!doNotClearAssets) clearMVSXFileAssets(plugin);
|
||||
|
||||
const archiveId = `ni,MurmurHash3_128;${murmurHash3_128_fromBytes(data, 42)}`;
|
||||
let files: { [path: string]: Uint8Array<ArrayBuffer> };
|
||||
@@ -160,7 +167,7 @@ export async function loadMVSData(plugin: PluginContext, data: MVSData | StringL
|
||||
throw new Error("loadMvsData: if `format` is 'mvsx', then `data` must be a Uint8Array or a base64-encoded string prefixed with 'base64,'.");
|
||||
}
|
||||
await plugin.runTask(Task.create('Load MVSX file', async ctx => {
|
||||
const parsed = await loadMVSX(plugin, ctx, data as Uint8Array<ArrayBuffer>);
|
||||
const parsed = await loadMVSX(plugin, ctx, data as Uint8Array<ArrayBuffer>, { doNotClearAssets: options?.appendSnapshots });
|
||||
await loadMVS(plugin, parsed.mvsData, { sanityChecks: true, ...options, sourceUrl: parsed.sourceUrl });
|
||||
}));
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2023-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ export const DefaultMultilayerColorThemeProps: MultilayerColorThemeProps = { lay
|
||||
* If a nested theme provider has `ensureCustomProperties` methods, these will not be called automatically
|
||||
* (the caller must ensure that any required custom properties be attached). */
|
||||
function makeMultilayerColorTheme(ctx: ThemeDataContext, props: MultilayerColorThemeProps, colorThemeRegistry: ColorTheme.Registry): ColorTheme<MultilayerColorThemeParams> {
|
||||
const { colorLayers, granularity } = makeLayers(ctx, props, colorThemeRegistry);
|
||||
const { colorLayers, granularity, preferSmoothing } = makeLayers(ctx, props, colorThemeRegistry);
|
||||
|
||||
function structureElementColor(loc: StructureElement.Location, isSecondary: boolean): Color {
|
||||
for (const layer of colorLayers) {
|
||||
@@ -101,7 +101,7 @@ function makeMultilayerColorTheme(ctx: ThemeDataContext, props: MultilayerColorT
|
||||
return {
|
||||
factory: (ctx_, props_) => makeMultilayerColorTheme(ctx_, props_, colorThemeRegistry),
|
||||
granularity,
|
||||
preferSmoothing: true,
|
||||
preferSmoothing,
|
||||
color: color,
|
||||
props: props,
|
||||
description: 'Combines colors from multiple color themes.',
|
||||
@@ -136,6 +136,7 @@ interface ColorLayer {
|
||||
function makeLayers(ctx: ThemeDataContext, props: MultilayerColorThemeProps, colorThemeRegistry: ColorTheme.Registry) {
|
||||
const colorLayers: ColorLayer[] = [];
|
||||
let granularityFlags = 0;
|
||||
let preferSmoothing = false;
|
||||
for (let i = props.layers.length - 1; i >= 0; i--) { // iterate from the end to get top layer first, bottom layer last
|
||||
const layer = props.layers[i];
|
||||
const themeProvider = colorThemeRegistry.get(layer.theme.name);
|
||||
@@ -175,8 +176,9 @@ function makeLayers(ctx: ThemeDataContext, props: MultilayerColorThemeProps, col
|
||||
default:
|
||||
console.warn(`Skipping color theme '${layer.theme.name}', cannot process granularity '${theme.granularity}'`);
|
||||
}
|
||||
if (theme.preferSmoothing) preferSmoothing = true;
|
||||
}
|
||||
return { colorLayers, granularity: granularityNameFromFlags(granularityFlags) };
|
||||
return { colorLayers, granularity: granularityNameFromFlags(granularityFlags), preferSmoothing };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -464,12 +464,15 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
|
||||
const refs = getPrimitiveStructureRefs(tree);
|
||||
const clip = clippingForNode(tree);
|
||||
const data = UpdateTarget.apply(updateParent, MVSInlinePrimitiveData, { node: tree as any });
|
||||
UpdateTarget.setMvsDependencies(data, refs); // MVSInlinePrimitiveData must depend on `refs` because it caches positions
|
||||
return applyPrimitiveVisuals(data, refs, clip);
|
||||
},
|
||||
primitives_from_uri(updateParent: UpdateTarget, tree: MolstarNode<'primitives_from_uri'>, context: MolstarLoadingContext): UpdateTarget {
|
||||
const data = UpdateTarget.apply(updateParent, MVSDownloadPrimitiveData, { uri: tree.params.uri, format: tree.params.format });
|
||||
const refs = new Set(tree.params.references);
|
||||
const clip = clippingForNode(tree);
|
||||
return applyPrimitiveVisuals(data, new Set(tree.params.references), clip);
|
||||
const data = UpdateTarget.apply(updateParent, MVSDownloadPrimitiveData, { uri: tree.params.uri, format: tree.params.format });
|
||||
UpdateTarget.setMvsDependencies(data, refs); // MVSInlinePrimitiveData must depend on `refs` because it caches positions
|
||||
return applyPrimitiveVisuals(data, refs, clip);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra';
|
||||
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Scene } from '../mol-gl/scene';
|
||||
import { assertUnreachable } from '../mol-util/type-helpers';
|
||||
import { Ray3D } from '../mol-math/geometry/primitives/ray3d';
|
||||
import { Mat4 } from '../mol-math/linear-algebra/3d/mat4';
|
||||
import { Vec4 } from '../mol-math/linear-algebra/3d/vec4';
|
||||
import { Vec3 } from '../mol-math/linear-algebra/3d/vec3';
|
||||
import { EPSILON } from '../mol-math/linear-algebra/3d/common';
|
||||
|
||||
export type { ICamera };
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
*/
|
||||
|
||||
import { Camera } from '../camera';
|
||||
import { Quat, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { lerp } from '../../mol-math/interpolate';
|
||||
import { Quat } from '../../mol-math/linear-algebra/3d/quat';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
|
||||
|
||||
export { CameraTransitionManager };
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
|
||||
import { Vec4 } from '../../mol-math/linear-algebra/3d/vec4';
|
||||
|
||||
export { Viewport };
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export const Canvas3DParams = {
|
||||
cameraClipping: PD.Group({
|
||||
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
|
||||
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
|
||||
minNear: PD.Numeric(5, { min: 0.1, max: 100, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
|
||||
minNear: PD.Numeric(1, { min: 0.1, max: 100, step: 0.1 }, { description: 'Minimal allowed distance of near clipping plane from the camera. Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
|
||||
}, { pivot: 'radius' }),
|
||||
viewport: PD.MappedStatic('canvas', {
|
||||
canvas: PD.Group({}),
|
||||
|
||||
@@ -75,7 +75,7 @@ export const TrackballControlsParams = {
|
||||
animate: PD.MappedStatic('off', {
|
||||
off: PD.EmptyGroup(),
|
||||
spin: PD.Group({
|
||||
speed: PD.Numeric(0.3, { min: -5, max: 5, step: 0.1 }, { description: 'Number of rotations per second' }),
|
||||
speed: PD.Numeric(0.1, { min: -2, max: 2, step: 0.01 }, { description: 'Number of rotations per second' }),
|
||||
}, { description: 'Spin the 3D scene around the x-axis in view space' }),
|
||||
rock: PD.Group({
|
||||
speed: PD.Numeric(0.3, { min: -5, max: 5, step: 0.1 }, { description: 'Number of oscilations per second' }),
|
||||
|
||||
@@ -163,7 +163,7 @@ export class IlluminationPass {
|
||||
const { gl, state } = this.webgl;
|
||||
|
||||
const markingEnabled = MarkingPass.isEnabled(props.marking);
|
||||
const hasTransparent = scene.opacityAverage < 1;
|
||||
const hasTransparent = scene.opacityAverage < 1 || scene.volumes.renderables.length > 0;
|
||||
const hasMarking = markingEnabled && scene.markerAverage > 0;
|
||||
|
||||
this.transparentTarget.bind();
|
||||
@@ -347,7 +347,7 @@ export class IlluminationPass {
|
||||
const dofEnabled = DofPass.isEnabled(props.postprocessing);
|
||||
|
||||
const markingEnabled = MarkingPass.isEnabled(props.marking);
|
||||
const hasTransparent = scene.opacityAverage < 1;
|
||||
const hasTransparent = scene.opacityAverage < 1 || scene.volumes.renderables.length > 0;
|
||||
const hasMarking = markingEnabled && scene.markerAverage > 0;
|
||||
|
||||
let needsUpdateCompose = false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { EquivalenceClasses } from '../util';
|
||||
import { EquivalenceClasses } from '../util/equivalence-classes';
|
||||
|
||||
describe('equiv-classes', () => {
|
||||
it('integer mod classes', () => {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
import * as ColumnHelpers from './column-helpers';
|
||||
import { Tensor as Tensors } from '../../mol-math/linear-algebra';
|
||||
import { Tokens } from '../../mol-io/reader/common/text/tokenizer';
|
||||
import { Tensor as Tensors } from '../../mol-math/linear-algebra/tensor';
|
||||
import type { Tokens } from '../../mol-io/reader/common/text/tokenizer';
|
||||
import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../mol-io/reader/common/text/number-parser';
|
||||
|
||||
interface Column<T> {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Column } from './column';
|
||||
import { sortArray } from '../util/sort';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { StringBuilder } from '../../mol-util/string-builder';
|
||||
|
||||
/** A collection of columns */
|
||||
type Table<Schema extends Table.Schema = any> = {
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { sortArray, hash3, hash4, createRangeArray } from '../../util';
|
||||
import { createRangeArray } from '../../util/array';
|
||||
import { hash3, hash4 } from '../../util/hash-functions';
|
||||
import { sortArray } from '../../util/sort';
|
||||
import { Interval } from '../interval';
|
||||
|
||||
type Nums = ArrayLike<number>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { iterableToArray } from '../util';
|
||||
import { iterableToArray } from '../util/array';
|
||||
|
||||
// TODO: rename to "linear map" and just do key value mapping from index?
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Segmentation, OrderedSet, SortedArray, Interval } from '../int';
|
||||
import { Iterator as _Iterator } from '../iterator';
|
||||
import { Interval } from './interval';
|
||||
import { OrderedSet } from './ordered-set';
|
||||
import { Segmentation } from './segmentation';
|
||||
import { SortedArray } from './sorted-array';
|
||||
|
||||
/** Pairs of min and max indices of sorted, non-overlapping ranges */
|
||||
type SortedRanges<T extends number = number> = SortedArray<T>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { hash2 } from '../util';
|
||||
import { hash2 } from '../util/hash-functions';
|
||||
|
||||
/**
|
||||
* Represents a pair of two integers as a double,
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Interval, OrderedSet, SortedArray } from '../../int';
|
||||
import { Interval } from '../../int/interval';
|
||||
import { OrderedSet } from '../../int/ordered-set';
|
||||
import { SortedArray } from '../../int/sorted-array';
|
||||
import { IntervalIterator } from '../interval-iterator';
|
||||
|
||||
describe('interval', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Column } from '../db';
|
||||
import { Column } from '../db/column';
|
||||
|
||||
export interface Grouping<V, K> {
|
||||
map: Map<K, V[]>,
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Interval } from '../int/interval';
|
||||
import { OrderedSet } from '../int/ordered-set';
|
||||
import { Segmentation } from '../int/segmentation';
|
||||
import { Iterator } from '../iterator';
|
||||
import { OrderedSet, Interval, Segmentation } from '../int';
|
||||
|
||||
/** Emits a segment of length one for each element in the interval that is also in the set */
|
||||
export class IntervalIterator<I extends number = number> implements Iterator<Segmentation.Segment<I>> {
|
||||
|
||||
@@ -122,7 +122,9 @@ export function createValueColor(value: Color, colorData?: ColorData): ColorData
|
||||
|
||||
/** Creates color uniform */
|
||||
function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
return createValueColor(color(NullLocation, false), colorData);
|
||||
locationIt.reset();
|
||||
const loc = locationIt.hasNext ? locationIt.move() : { location: NullLocation, isSecondary: false };
|
||||
return createValueColor(color(loc.location, loc.isSecondary), colorData);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
|
||||
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
|
||||
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
import { LocationIterator } from '../../util/location-iterator';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { calculateTransformBoundingSphere, createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Lines } from './lines';
|
||||
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
import { Cage } from '../../primitive/cage';
|
||||
|
||||
export interface LinesBuilder {
|
||||
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Mesh } from './mesh';
|
||||
import { Primitive } from '../../primitive/primitive';
|
||||
import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
import { Cage } from '../../primitive/cage';
|
||||
import { addSphere } from './builder/sphere';
|
||||
import { addCylinder } from './builder/cylinder';
|
||||
|
||||
|
||||
@@ -77,7 +77,9 @@ export function createValueSize(value: number, sizeData?: SizeData): SizeData {
|
||||
|
||||
/** Creates size uniform */
|
||||
export function createUniformSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
|
||||
return createValueSize(sizeFn(NullLocation), sizeData);
|
||||
locationIt.reset();
|
||||
const location = locationIt.hasNext ? locationIt.move().location : NullLocation;
|
||||
return createValueSize(sizeFn(location), sizeData);
|
||||
}
|
||||
|
||||
export function createTextureSize(sizes: TextureImage<Uint8Array>, type: SizeType, sizeData?: SizeData): SizeData {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ValueCell } from '../../../mol-util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
|
||||
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { SpheresValues } from '../../../mol-gl/renderable/spheres';
|
||||
import { createColors } from '../color-data';
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
|
||||
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { createColors } from '../color-data';
|
||||
@@ -333,5 +334,5 @@ function getPadding(mappings: Float32Array, depths: Float32Array, charCount: num
|
||||
const d = Math.abs(depths[i]);
|
||||
if (d > maxDepth) maxDepth = d;
|
||||
}
|
||||
return scale * Math.max(maxDepth, maxOffset);
|
||||
return Math.max(maxDepth, scale * maxOffset);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
|
||||
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
|
||||
@@ -786,5 +786,6 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
|
||||
stencilOp: function () { },
|
||||
stencilOpSeparate: function () { },
|
||||
unpackColorSpace: 'srgb',
|
||||
makeXRCompatible: async function () { },
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { LinkedList } from '../mol-data/generic';
|
||||
import { LinkedList } from '../mol-data/generic/linked-list';
|
||||
import { GraphicsRenderObject } from './render-object';
|
||||
|
||||
type N = LinkedList.Node<GraphicsRenderObject>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -38,6 +38,7 @@ const IsosurfaceSchema = {
|
||||
|
||||
uGridDim: UniformSpec('v3'),
|
||||
uGridTexDim: UniformSpec('v3'),
|
||||
uGridDataDim: UniformSpec('v3'),
|
||||
uGridTransform: UniformSpec('m4'),
|
||||
uGridTransformAdjoint: UniformSpec('m3'),
|
||||
uScale: UniformSpec('v2'),
|
||||
@@ -54,7 +55,7 @@ function valueChannel(ctx: WebGLContext, volumeData: Texture) {
|
||||
return isWebGL2(ctx.gl) && volumeData.format === ctx.gl.RED ? 'red' : 'alpha';
|
||||
}
|
||||
|
||||
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, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
|
||||
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
|
||||
if (ctx.namedComputeRenderables[IsosurfaceName]) {
|
||||
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
|
||||
|
||||
@@ -71,6 +72,7 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
|
||||
|
||||
ValueCell.update(v.uGridDim, gridDim);
|
||||
ValueCell.update(v.uGridTexDim, gridTexDim);
|
||||
ValueCell.update(v.uGridDataDim, gridDataDim);
|
||||
ValueCell.update(v.uGridTransform, transform);
|
||||
ValueCell.update(v.uGridTransformAdjoint, Mat3.adjointFromMat4(Mat3(), transform));
|
||||
ValueCell.update(v.uScale, scale);
|
||||
@@ -81,12 +83,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, invert, packedGroup, axisOrder, constantGroup);
|
||||
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, gridDataDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
|
||||
}
|
||||
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, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
|
||||
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
|
||||
// console.log('uSize', Math.pow(2, levels))
|
||||
const values: IsosurfaceValues = {
|
||||
...QuadValues,
|
||||
@@ -105,6 +107,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
|
||||
|
||||
uGridDim: ValueCell.create(gridDim),
|
||||
uGridTexDim: ValueCell.create(gridTexDim),
|
||||
uGridDataDim: ValueCell.create(gridDataDim),
|
||||
uGridTransform: ValueCell.create(transform),
|
||||
uGridTransformAdjoint: ValueCell.create(Mat3.adjointFromMat4(Mat3(), transform)),
|
||||
uScale: ValueCell.create(scale),
|
||||
@@ -132,7 +135,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, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
@@ -189,7 +192,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, invert, packedGroup, axisOrder, constantGroup);
|
||||
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, gridDataDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
|
||||
framebuffer.bind();
|
||||
@@ -225,11 +228,11 @@ 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, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
if (isTimingMode) ctx.timer.mark('extractIsosurface');
|
||||
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
|
||||
const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
|
||||
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
|
||||
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, gridDataDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
|
||||
if (isTimingMode) ctx.timer.markEnd('extractIsosurface');
|
||||
|
||||
return gv;
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../mol-math/linear-algebra';
|
||||
import { Mat4 } from '../mol-math/linear-algebra/3d/mat4';
|
||||
import { Vec3 } from '../mol-math/linear-algebra/3d/vec3';
|
||||
|
||||
export interface Object3D {
|
||||
readonly view: Mat4
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
import { Program } from './webgl/program';
|
||||
import { RenderableValues, Values, RenderableSchema, BaseValues } from './renderable/schema';
|
||||
import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant, MultiDrawBaseData, Transparency } from './webgl/render-item';
|
||||
import { ValueCell } from '../mol-util';
|
||||
import { ValueCell } from '../mol-util/value-cell';
|
||||
import { idFactory } from '../mol-util/id-factory';
|
||||
import { clamp } from '../mol-math/interpolate';
|
||||
import { Frustum3D } from '../mol-math/geometry/primitives/frustum3d';
|
||||
import { Plane3D } from '../mol-math/geometry/primitives/plane3d';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { Sphere3D } from '../mol-math/geometry/primitives/sphere3d';
|
||||
import { Vec4 } from '../mol-math/linear-algebra/3d/vec4';
|
||||
import { WebGLStats } from './webgl/context';
|
||||
import { isTimingMode } from '../mol-util/debug';
|
||||
|
||||
@@ -9,8 +9,7 @@ import { WebGLContext } from '../webgl/context';
|
||||
import { createGraphicsRenderItem, Transparency } from '../webgl/render-item';
|
||||
import { AttributeSpec, Values, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, BaseSchema, UniformSpec, GlobalTextureSchema, GlobalDefineValues, GlobalDefines, GlobalDefineSchema } from './schema';
|
||||
import { ImageShaderCode } from '../shader-code';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { InterpolationTypeNames } from '../../mol-geo/geometry/image/image';
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
|
||||
export const ImageSchema = {
|
||||
...BaseSchema,
|
||||
@@ -33,7 +32,8 @@ export const ImageSchema = {
|
||||
|
||||
uIsoLevel: UniformSpec('f'),
|
||||
|
||||
dInterpolation: DefineSpec('string', InterpolationTypeNames),
|
||||
/** Same as `InterpolationTypeNames` in '../../mol-geo/geometry/image/image' */
|
||||
dInterpolation: DefineSpec('string', ['nearest', 'catmulrom', 'mitchell', 'bspline']),
|
||||
};
|
||||
export type ImageSchema = typeof ImageSchema
|
||||
export type ImageValues = Values<ImageSchema>
|
||||
|
||||
@@ -22,6 +22,7 @@ uniform bool uInvert;
|
||||
|
||||
uniform vec3 uGridDim;
|
||||
uniform vec3 uGridTexDim;
|
||||
uniform vec3 uGridDataDim;
|
||||
uniform mat4 uGridTransform;
|
||||
uniform mat3 uGridTransformAdjoint;
|
||||
|
||||
@@ -93,20 +94,19 @@ vec4 baseVoxel(vec2 pos) {
|
||||
}
|
||||
|
||||
vec4 getGroup(const in vec3 p) {
|
||||
vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding
|
||||
// note that we swap x and z because the texture is flipped around y
|
||||
#if defined(dAxisOrder_012)
|
||||
float group = p.z + p.y * gridDim.z + p.x * gridDim.z * gridDim.y; // 210
|
||||
float group = p.z + p.y * uGridDataDim.z + p.x * uGridDataDim.z * uGridDataDim.y; // 210
|
||||
#elif defined(dAxisOrder_021)
|
||||
float group = p.y + p.z * gridDim.y + p.x * gridDim.y * gridDim.z; // 120
|
||||
float group = p.y + p.z * uGridDataDim.y + p.x * uGridDataDim.y * uGridDataDim.z; // 120
|
||||
#elif defined(dAxisOrder_102)
|
||||
float group = p.z + p.x * gridDim.z + p.y * gridDim.z * gridDim.x; // 201
|
||||
float group = p.z + p.x * uGridDataDim.z + p.y * uGridDataDim.z * uGridDataDim.x; // 201
|
||||
#elif defined(dAxisOrder_120)
|
||||
float group = p.x + p.z * gridDim.x + p.y * gridDim.x * gridDim.z; // 021
|
||||
float group = p.x + p.z * uGridDataDim.x + p.y * uGridDataDim.x * uGridDataDim.z; // 021
|
||||
#elif defined(dAxisOrder_201)
|
||||
float group = p.y + p.x * gridDim.y + p.z * gridDim.y * gridDim.x; // 102
|
||||
float group = p.y + p.x * uGridDataDim.y + p.z * uGridDataDim.y * uGridDataDim.x; // 102
|
||||
#elif defined(dAxisOrder_210)
|
||||
float group = p.x + p.y * gridDim.x + p.z * gridDim.x * gridDim.y; // 012
|
||||
float group = p.x + p.y * uGridDataDim.x + p.z * uGridDataDim.x * uGridDataDim.y; // 012
|
||||
#endif
|
||||
return vec4(group > 16777215.5 ? vec3(1.0) : packIntToRGB(group), 1.0);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -48,7 +48,7 @@ vec2 getDepthTransparentWithAlpha(const in vec2 coords) {
|
||||
}
|
||||
|
||||
bool isBackground(const in float depth) {
|
||||
return depth > 0.9999;
|
||||
return depth == 1.0;
|
||||
}
|
||||
|
||||
float getPixelSize(const in vec2 coords, const in float depth) {
|
||||
@@ -108,8 +108,8 @@ void main(void) {
|
||||
transparentOutlineFlag = 0.0;
|
||||
bestTransparentAlpha = 0.0;
|
||||
}
|
||||
|
||||
vec2 depthPacked; // Pack depth in G/B channels
|
||||
|
||||
vec2 depthPacked; // Pack depth in G/B channels
|
||||
float outlineTypeFlag = 0.0;
|
||||
if (opaqueOutlineFlag > 0.0 && transparentOutlineFlag > 0.0) {
|
||||
outlineTypeFlag = 0.75; // Both
|
||||
@@ -121,7 +121,7 @@ void main(void) {
|
||||
outlineTypeFlag = 0.25; // Opaque only
|
||||
depthPacked = packUnitIntervalToRG(bestOpaqueDepth);
|
||||
}
|
||||
|
||||
|
||||
float alpha = clamp(bestTransparentAlpha, 0.0, 0.5) * 2.0; // limiting to range [0.0, 0.5] to improve alpha precision since we don't need a wider range
|
||||
float packedFlagWithAlpha = pack2x4(vec2(outlineTypeFlag, alpha)); // pack outlineType with alpha
|
||||
gl_FragColor = vec4(packedFlagWithAlpha, depthPacked.x, depthPacked.y, bestTransparentDepth);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -54,13 +54,13 @@ float getDepthTransparent(const in vec2 coords) {
|
||||
}
|
||||
|
||||
bool isBackground(const in float depth) {
|
||||
return depth > 0.9999; // handle depth packing precision issues
|
||||
return depth == 1.0;
|
||||
}
|
||||
|
||||
int squaredOutlineScale = dOutlineScale * dOutlineScale;
|
||||
void getOutline(const in vec2 coords, out bool hasOpaque, out bool hasTransparent, out float opaqueDepth, out float transparentDepth, out float alpha) {
|
||||
vec2 invTexSize = 1.0 / uTexSize;
|
||||
|
||||
|
||||
hasOpaque = false;
|
||||
hasTransparent = false;
|
||||
opaqueDepth = 1.0;
|
||||
@@ -81,14 +81,14 @@ void getOutline(const in vec2 coords, out bool hasOpaque, out bool hasTransparen
|
||||
|
||||
float sampleFlag = sampleFlagWithAlpha.x;
|
||||
float sampleAlpha = clamp(sampleFlagWithAlpha.y * 0.5, 0.01, 1.0);
|
||||
|
||||
|
||||
if ((sampleFlag > 0.20 && sampleFlag < 0.30) || (sampleFlag > 0.70 && sampleFlag < 0.80)) { // transparent || both
|
||||
if (sampleOpaqueDepth < opaqueDepth) {
|
||||
hasOpaque = true;
|
||||
opaqueDepth = sampleOpaqueDepth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((((sampleFlag > 0.45 && sampleFlag < 0.55) || (sampleFlag > 0.70 && sampleFlag < 0.80))) && sampleTransparentDepth < transparentDepth) { // transparent || both
|
||||
hasTransparent = true;
|
||||
transparentDepth = sampleTransparentDepth;
|
||||
@@ -184,15 +184,15 @@ void main(void) {
|
||||
if (hasOpaque) {
|
||||
float viewDist = abs(getViewZ(outlineOpaqueDepth));
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
|
||||
if (!uTransparentBackground) {
|
||||
if (!uTransparentBackground) {
|
||||
color.rgb = mix(uOutlineColor, uFogColor, fogFactor);
|
||||
} else {
|
||||
color.a = 1.0 - fogFactor;
|
||||
color.rgb = mix(uOutlineColor, vec3(0.0), fogFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef dBlendTransparency
|
||||
#ifdef dBlendTransparency
|
||||
if (hasTransparent) {
|
||||
if (hasOpaque && outlineOpaqueDepth < outlineTransparentDepth) {
|
||||
blendTransparency = false;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Ludovic Autin <ludovic.autin@gmail.com>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
export const ssaoBlur_frag = `
|
||||
@@ -36,8 +37,7 @@ float getViewZ(const in float depth) {
|
||||
}
|
||||
|
||||
bool isBackground(const in float depth) {
|
||||
// checking for 1.0 is not enough, because of precision issues
|
||||
return depth >= 0.999;
|
||||
return depth == 1.0;
|
||||
}
|
||||
|
||||
bool isNearClip(const in float depth) {
|
||||
@@ -78,8 +78,9 @@ void main(void) {
|
||||
|
||||
float sum = 0.0;
|
||||
float kernelSum = 0.0;
|
||||
int halfKernelSize = dOcclusionKernelSize / 2;
|
||||
// only if kernelSize is odd
|
||||
for (int i = -dOcclusionKernelSize / 2; i <= dOcclusionKernelSize / 2; i++) {
|
||||
for (int i = -halfKernelSize; i <= halfKernelSize; i++) {
|
||||
if (abs(float(i)) > 1.0 && abs(float(i)) * pixelSize > 0.8) continue;
|
||||
|
||||
vec2 sampleCoords = coords + float(i) * offset;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
@@ -64,7 +64,7 @@ vec2 getNoiseVec2(const in vec2 coords) {
|
||||
}
|
||||
|
||||
bool isBackground(const in float depth) {
|
||||
return depth > 0.999; // handle precision issues with packed depth
|
||||
return depth == 1.0;
|
||||
}
|
||||
|
||||
float getDepth(const in vec2 coords, const in int transparentFlag) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -40,7 +40,7 @@ export const CifCore_Schema = {
|
||||
* in exceptional circumstances (Brock, 2025) may be reported as
|
||||
* non-integral.
|
||||
*
|
||||
* Reference: Brock, C. P. (2025). Acta Cryst. A81, nnn-nnn.
|
||||
* Reference: Brock, C. P. (2025). Acta Cryst. A81, 405-408.
|
||||
*/
|
||||
formula_units_z: float,
|
||||
/**
|
||||
@@ -336,7 +336,12 @@ export const CifCore_Schema = {
|
||||
*/
|
||||
site_symmetry_2: str,
|
||||
/**
|
||||
* Bond valence calculated from the bond distance.
|
||||
* Valence assigned to the bond between the sites identified by
|
||||
* _geom_bond.id calculated according to the bond-valence model
|
||||
* (Brown, 2002) from the bond distance.
|
||||
*
|
||||
* Ref: Brown, I. D. (2002). The Chemical Bond in Inorganic Chemistry:
|
||||
* the Bond-Valence Model, eq. (3.1). Oxford: Oxford University Press.
|
||||
*/
|
||||
valence: float,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
@@ -5078,7 +5078,7 @@ export const mmCIF_Schema = {
|
||||
/**
|
||||
* The type of data held in the dataset.
|
||||
*/
|
||||
content_type: Aliased<'target' | 'template structure' | 'polymeric template library' | 'spatial restraints' | 'target-template alignment' | 'coevolution MSA' | 'model coordinates' | 'input structure' | 'reference database' | 'other'>(str),
|
||||
content_type: Aliased<'target' | 'template structure' | 'polymeric template library' | 'spatial restraints' | 'target-template alignment' | 'coevolution MSA' | 'model coordinates' | 'input structure' | 'reference database' | 'intermediate backbone' | 'intermediate sequence' | 'model quality assessment scores' | 'energy estimate' | 'experimental validation' | 'other'>(str),
|
||||
/**
|
||||
* Details for other content types.
|
||||
*/
|
||||
@@ -5131,7 +5131,7 @@ export const mmCIF_Schema = {
|
||||
/**
|
||||
* The mode of calculation of the QA metric.
|
||||
*/
|
||||
mode: Aliased<'local' | 'global' | 'local-pairwise' | 'per-feature' | 'per-feature-pair'>(str),
|
||||
mode: Aliased<'local' | 'global' | 'local-pairwise' | 'per-feature' | 'per-feature-pair' | 'dihedral'>(str),
|
||||
/**
|
||||
* Identifier to the set of software used to calculate the QA metric.
|
||||
* This data item is a pointer to the _ma_software_group.group_id in the
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { chunkedSubtask, RuntimeContext } from '../../../../mol-task';
|
||||
import { RuntimeContext } from '../../../../mol-task/execution/runtime-context';
|
||||
import { chunkedSubtask } from '../../../../mol-task/util/chunked';
|
||||
import { StringLike } from '../../../common/string-like';
|
||||
|
||||
export { Tokenizer };
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
import { GridLookup3D } from '../../geometry';
|
||||
import { sortArray } from '../../../mol-data/util';
|
||||
import { OrderedSet } from '../../../mol-data/int';
|
||||
import { sortArray } from '../../../mol-data/util/sort';
|
||||
import { OrderedSet } from '../../../mol-data/int/ordered-set';
|
||||
import { getBoundary } from '../boundary';
|
||||
|
||||
const xs = [0, 0, 1];
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { PositionData } from './common';
|
||||
import { Vec3 } from '../linear-algebra';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
import { BoundaryHelper } from './boundary-helper';
|
||||
import { Box3D } from '../geometry/primitives/box3d';
|
||||
import { Sphere3D } from '../geometry/primitives/sphere3d';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
|
||||
import { Vec3 } from '../linear-algebra/3d/vec3';
|
||||
import { Sphere3D } from './primitives/sphere3d';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2025 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>
|
||||
*/
|
||||
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { Mat4, Tensor, Vec3, Vec2 } from '../linear-algebra';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
import { Mat4 } from '../linear-algebra/3d/mat4';
|
||||
import { Vec3 } from '../linear-algebra/3d/vec3';
|
||||
import { Tensor } from '../linear-algebra/tensor';
|
||||
import { Box3D } from './primitives/box3d';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export interface PositionData {
|
||||
x: ArrayLike<number>,
|
||||
@@ -30,15 +31,6 @@ export type DensityData = {
|
||||
maxRadius: number,
|
||||
}
|
||||
|
||||
export type DensityTextureData = {
|
||||
transform: Mat4,
|
||||
texture: Texture,
|
||||
bbox: Box3D,
|
||||
gridDim: Vec3,
|
||||
gridTexDim: Vec3
|
||||
gridTexScale: Vec2
|
||||
}
|
||||
|
||||
export interface RegularGrid3d {
|
||||
box: Box3D,
|
||||
dimensions: Vec3
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Box3D, DensityData, DensityTextureData } from '../geometry';
|
||||
import { Box3D, DensityData } from '../geometry';
|
||||
import { PositionData } from './common';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { GaussianDensityTexture2d, GaussianDensityTexture3d } from './gaussian-density/gpu';
|
||||
import { Task } from '../../mol-task/task';
|
||||
import { GaussianDensityCPU } from './gaussian-density/cpu';
|
||||
import { Mat4 } from '../linear-algebra/3d/mat4';
|
||||
import { Vec3 } from '../linear-algebra/3d/vec3';
|
||||
import { Vec2 } from '../linear-algebra/3d/vec2';
|
||||
|
||||
export const DefaultGaussianDensityProps = {
|
||||
resolution: 1,
|
||||
@@ -27,7 +30,14 @@ export type GaussianDensityTextureData = {
|
||||
radiusFactor: number
|
||||
resolution: number
|
||||
maxRadius: number
|
||||
} & DensityTextureData
|
||||
transform: Mat4,
|
||||
texture: Texture,
|
||||
bbox: Box3D,
|
||||
gridDim: Vec3,
|
||||
gridTexDim: Vec3
|
||||
gridDataDim: Vec3
|
||||
gridTexScale: Vec2
|
||||
}
|
||||
|
||||
export function computeGaussianDensity(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) {
|
||||
return Task.create('Gaussian Density', async ctx => {
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Box3D, fillGridDim } from '../../geometry';
|
||||
import { Vec3, Mat4, Tensor } from '../../linear-algebra';
|
||||
import { RuntimeContext } from '../../../mol-task';
|
||||
import { PositionData } from '../common';
|
||||
import { OrderedSet } from '../../../mol-data/int';
|
||||
import { fillGridDim, PositionData } from '../common';
|
||||
import { OrderedSet } from '../../../mol-data/int/ordered-set';
|
||||
import { GaussianDensityProps, GaussianDensityData } from '../gaussian-density';
|
||||
import { fasterExp } from '../../approx';
|
||||
import { Box3D } from '../primitives/box3d';
|
||||
import { Vec3 } from '../../linear-algebra/3d/vec3';
|
||||
import { Tensor } from '../../linear-algebra/tensor';
|
||||
import { Mat4 } from '../../linear-algebra/3d/mat4';
|
||||
|
||||
export async function GaussianDensityCPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<GaussianDensityData> {
|
||||
const { resolution, radiusOffset, smoothness } = props;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Michael Krone <michael.krone@uni-tuebingen.de>
|
||||
@@ -99,8 +99,8 @@ export function GaussianDensityTexture3d(webgl: WebGLContext, position: Position
|
||||
return finalizeGaussianDensityTexture(data);
|
||||
}
|
||||
|
||||
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius }: _GaussianDensityTextureData): GaussianDensityTextureData {
|
||||
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridDataDim, gridTexScale, radiusFactor, resolution, maxRadius }: _GaussianDensityTextureData): GaussianDensityTextureData {
|
||||
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridDataDim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
}
|
||||
|
||||
function getTransform(scale: Vec3, bbox: Box3D) {
|
||||
@@ -118,6 +118,7 @@ type _GaussianDensityTextureData = {
|
||||
bbox: Box3D,
|
||||
gridDim: Vec3,
|
||||
gridTexDim: Vec3
|
||||
gridDataDim: Vec3
|
||||
gridTexScale: Vec2
|
||||
radiusFactor: number
|
||||
resolution: number
|
||||
@@ -206,7 +207,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
|
||||
|
||||
// printTextureImage(readTexture(webgl, minDistTex), { scale: 0.75 });
|
||||
|
||||
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridDataDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
}
|
||||
|
||||
function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture): _GaussianDensityTextureData {
|
||||
@@ -262,7 +263,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
|
||||
setupGroupIdRendering(webgl, renderable);
|
||||
render(texture, false);
|
||||
|
||||
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridDataDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -7,15 +7,16 @@
|
||||
* ported from NGL (https://github.com/arose/ngl), licensed under MIT
|
||||
*/
|
||||
|
||||
import { Vec3, Tensor } from '../../mol-math/linear-algebra';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { PositionData } from './common';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
|
||||
import { Box3D, GridLookup3D, fillGridDim } from '../../mol-math/geometry';
|
||||
import { BaseGeometry } from '../../mol-geo/geometry/base';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
import { fillGridDim, PositionData } from './common';
|
||||
import { Boundary } from './boundary';
|
||||
import { GridLookup3D } from './lookup3d/grid';
|
||||
import { Box3D } from './primitives/box3d';
|
||||
import { Vec3 } from '../linear-algebra/3d/vec3';
|
||||
import { Tensor } from '../linear-algebra/tensor';
|
||||
import { Mat4 } from '../linear-algebra/3d/mat4';
|
||||
|
||||
function normalToLine(out: Vec3, p: Vec3) {
|
||||
out[0] = out[1] = out[2] = 1.0;
|
||||
@@ -48,8 +49,8 @@ function getAngleTables(probePositions: number): AnglesTables {
|
||||
|
||||
export const MolecularSurfaceCalculationParams = {
|
||||
probeRadius: PD.Numeric(1.4, { min: 0, max: 10, step: 0.1 }, { description: 'Radius of the probe tracing the molecular surface.' }),
|
||||
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }, { description: 'Grid resolution/cell spacing.', ...BaseGeometry.CustomQualityParamInfo }),
|
||||
probePositions: PD.Numeric(36, { min: 12, max: 90, step: 1 }, { description: 'Number of positions tested for probe target intersection.', ...BaseGeometry.CustomQualityParamInfo }),
|
||||
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }, { description: 'Grid resolution/cell spacing.' }),
|
||||
probePositions: PD.Numeric(36, { min: 12, max: 90, step: 1 }, { description: 'Number of positions tested for probe target intersection.' }),
|
||||
};
|
||||
export const DefaultMolecularSurfaceCalculationProps = PD.getDefaultValues(MolecularSurfaceCalculationParams);
|
||||
export type MolecularSurfaceCalculationProps = typeof DefaultMolecularSurfaceCalculationProps
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { PositionData } from '../common';
|
||||
import { OrderedSet } from '../../../mol-data/int';
|
||||
import { OrderedSet } from '../../../mol-data/int/ordered-set';
|
||||
import { Sphere3D } from './sphere3d';
|
||||
import { Vec3 } from '../../linear-algebra/3d/vec3';
|
||||
import { Mat4 } from '../../linear-algebra/3d/mat4';
|
||||
|
||||
@@ -5,13 +5,15 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4, EPSILON } from '../../linear-algebra';
|
||||
import { PositionData } from '../common';
|
||||
import { OrderedSet } from '../../../mol-data/int';
|
||||
import { OrderedSet } from '../../../mol-data/int/ordered-set';
|
||||
import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
|
||||
import { Box3D } from './box3d';
|
||||
import { Axes3D } from './axes3d';
|
||||
import { PrincipalAxes } from '../../linear-algebra/matrix/principal-axes';
|
||||
import { Vec3 } from '../../linear-algebra/3d/vec3';
|
||||
import { Mat4 } from '../../linear-algebra/3d/mat4';
|
||||
import { EPSILON } from '../../linear-algebra/3d/common';
|
||||
|
||||
interface Sphere3D {
|
||||
center: Vec3,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { lerp as scalar_lerp } from '../../mol-math/interpolate';
|
||||
import { lerp as scalar_lerp } from '../interpolate';
|
||||
import { Mat3 } from '../linear-algebra/3d/mat3';
|
||||
import { Mat4 } from '../linear-algebra/3d/mat4';
|
||||
import { Quat } from '../linear-algebra/3d/quat';
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Mat3 {
|
||||
}
|
||||
|
||||
export function hasNaN(m: Mat3) {
|
||||
for (let i = 0; i < 9; i++) if (isNaN(m[i])) return true;
|
||||
for (let i = 0; i < 9; i++) if (Number.isNaN(m[i])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Mat4 {
|
||||
}
|
||||
|
||||
export function hasNaN(m: Mat4) {
|
||||
for (let i = 0; i < 16; i++) if (isNaN(m[i])) return true;
|
||||
for (let i = 0; i < 16; i++) if (Number.isNaN(m[i])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
import { Mat4 } from './mat4';
|
||||
import { Vec3 } from './vec3';
|
||||
import { EVD } from '../matrix/evd';
|
||||
import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
|
||||
import { Matrix } from '../matrix/matrix';
|
||||
import { Sphere3D } from '../../geometry/primitives/sphere3d';
|
||||
import { CentroidHelper } from '../../geometry/centroid-helper';
|
||||
|
||||
export { MinimizeRmsd };
|
||||
namespace MinimizeRmsd {
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Quat {
|
||||
}
|
||||
|
||||
export function hasNaN(q: Quat) {
|
||||
return isNaN(q[0]) || isNaN(q[1]) || isNaN(q[2]) || isNaN(q[3]);
|
||||
return Number.isNaN(q[0]) || Number.isNaN(q[1]) || Number.isNaN(q[2]) || Number.isNaN(q[3]);
|
||||
}
|
||||
|
||||
export function create(x: number, y: number, z: number, w: number) {
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Vec2 {
|
||||
}
|
||||
|
||||
export function hasNaN(a: Vec2) {
|
||||
return isNaN(a[0]) || isNaN(a[1]);
|
||||
return Number.isNaN(a[0]) || Number.isNaN(a[1]);
|
||||
}
|
||||
|
||||
export function toArray<T extends NumberArray>(a: Vec2, out: T, offset: number) {
|
||||
|
||||
@@ -52,8 +52,12 @@ export namespace Vec3 {
|
||||
return _isFinite(a[0]) && _isFinite(a[1]) && _isFinite(a[2]);
|
||||
}
|
||||
|
||||
export function isInteger(a: Vec3): boolean {
|
||||
return Number.isInteger(a[0]) && Number.isInteger(a[1]) && Number.isInteger(a[2]);
|
||||
}
|
||||
|
||||
export function hasNaN(a: Vec3) {
|
||||
return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]);
|
||||
return Number.isNaN(a[0]) || Number.isNaN(a[1]) || Number.isNaN(a[2]);
|
||||
}
|
||||
|
||||
export function setNaN(out: Vec3) {
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
|
||||
import { Mat4 } from './mat4';
|
||||
import { NumberArray } from '../../../mol-util/type-helpers';
|
||||
import { Sphere3D } from '../../geometry/primitives/sphere3d';
|
||||
import { EPSILON } from './common';
|
||||
|
||||
type SphereLike = { center: number[], radius: number };
|
||||
|
||||
interface Vec4 extends Array<number> { [d: number]: number, '@type': 'vec4', length: 4 }
|
||||
|
||||
function Vec4() {
|
||||
@@ -54,7 +55,7 @@ namespace Vec4 {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromSphere(out: Vec4, sphere: Sphere3D) {
|
||||
export function fromSphere(out: Vec4, sphere: SphereLike) {
|
||||
out[0] = sphere.center[0];
|
||||
out[1] = sphere.center[1];
|
||||
out[2] = sphere.center[2];
|
||||
@@ -62,12 +63,12 @@ namespace Vec4 {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function ofSphere(sphere: Sphere3D) {
|
||||
export function ofSphere(sphere: SphereLike) {
|
||||
return fromSphere(zero(), sphere);
|
||||
}
|
||||
|
||||
export function hasNaN(a: Vec4) {
|
||||
return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]) || isNaN(a[3]);
|
||||
return Number.isNaN(a[0]) || Number.isNaN(a[1]) || Number.isNaN(a[2]) || Number.isNaN(a[3]);
|
||||
}
|
||||
|
||||
export function toArray<T extends NumberArray>(a: Vec4, out: T, offset: number) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Mat3 } from './3d/mat3';
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
@@ -9,6 +8,7 @@ import { Mat3 } from './3d/mat3';
|
||||
import { Mat4 } from './3d/mat4';
|
||||
import { Vec3 } from './3d/vec3';
|
||||
import { Vec4 } from './3d/vec4';
|
||||
import { Mat3 } from './3d/mat3';
|
||||
|
||||
export interface Tensor { data: Tensor.Data, space: Tensor.Space }
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @author Yana Rose <yana.v.rose@gmail.com>
|
||||
*/
|
||||
|
||||
import { substringStartsWith } from '../../../mol-util/string';
|
||||
import { CifCategory, CifField, CifFrame } from '../../../mol-io/reader/cif';
|
||||
import { Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
|
||||
import { PdbFile } from '../../../mol-io/reader/pdb/schema';
|
||||
@@ -23,6 +22,16 @@ import { parseConect } from './conect';
|
||||
import { isDebugMode } from '../../../mol-util/debug';
|
||||
import { PdbHeaderData, addHeader } from './header';
|
||||
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { StringLike } from '../../../mol-io/common/string-like';
|
||||
|
||||
function substringStartsWith(str: StringLike, start: number, end: number, target: string) {
|
||||
const len = target.length;
|
||||
if (len > end - start) return false;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (str.charCodeAt(start + i) !== target.charCodeAt(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
const { lines } = pdb;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { Grid, Volume } from '../../mol-model/volume';
|
||||
import { Task } from '../../mol-task';
|
||||
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
|
||||
import { Mat4, Tensor, Vec3 } from '../../mol-math/linear-algebra';
|
||||
@@ -71,19 +71,22 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
|
||||
// always calculate stats when all stats related values are zero
|
||||
const calcStats = header.AMIN === 0 && header.AMAX === 0 && header.AMEAN === 0 && header.ARMS === 0;
|
||||
|
||||
const volgrid: Grid = {
|
||||
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3(), origin_frac, dimensions_frac)) },
|
||||
cells: data,
|
||||
stats: {
|
||||
min: (Number.isNaN(header.AMIN) || calcStats) ? arrayMin(values) : header.AMIN,
|
||||
max: (Number.isNaN(header.AMAX) || calcStats) ? arrayMax(values) : header.AMAX,
|
||||
mean: (Number.isNaN(header.AMEAN) || calcStats) ? arrayMean(values) : header.AMEAN,
|
||||
sigma: (Number.isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
label: params?.label,
|
||||
entryId: params?.entryId,
|
||||
grid: {
|
||||
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
|
||||
cells: data,
|
||||
stats: {
|
||||
min: (isNaN(header.AMIN) || calcStats) ? arrayMin(values) : header.AMIN,
|
||||
max: (isNaN(header.AMAX) || calcStats) ? arrayMax(values) : header.AMAX,
|
||||
mean: (isNaN(header.AMEAN) || calcStats) ? arrayMean(values) : header.AMEAN,
|
||||
sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
|
||||
},
|
||||
},
|
||||
periodicity: Vec3.isInteger(dimensions_frac) ? 'xyz' : 'none',
|
||||
grid: volgrid,
|
||||
instances: [{ transform: Mat4.identity() }],
|
||||
sourceData: Ccp4Format.create(source),
|
||||
customProperties: new CustomProperties(),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2025 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>
|
||||
*/
|
||||
|
||||
import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
|
||||
@@ -38,6 +39,7 @@ export function volumeFromDensityServerData(source: DensityServer_Data_Database,
|
||||
return {
|
||||
label: params?.label,
|
||||
entryId: params?.entryId,
|
||||
periodicity: Vec3.isInteger(dimensions) ? 'xyz' : 'none',
|
||||
grid: {
|
||||
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
|
||||
cells: data,
|
||||
|
||||
@@ -36,6 +36,7 @@ export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, la
|
||||
return {
|
||||
label: params?.label,
|
||||
entryId: params?.entryId,
|
||||
periodicity: Vec3.isInteger(dimensions_frac) ? 'xyz' : 'none',
|
||||
grid: {
|
||||
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
|
||||
cells: data,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2025 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>
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { UUID } from '../../../mol-util/uuid';
|
||||
import { StructureSequence } from './properties/sequence';
|
||||
import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic';
|
||||
import { AtomicHierarchy, AtomicConformation, AtomicRanges, VdwRadius } from './properties/atomic';
|
||||
import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
|
||||
import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
|
||||
import { CustomProperties } from '../../custom-property';
|
||||
@@ -184,6 +185,20 @@ export namespace Model {
|
||||
return center;
|
||||
}
|
||||
|
||||
const AtomicRadiiProp = '__AtomicRadii__';
|
||||
/** Get array of atomic radii for all atoms in the model (cached). */
|
||||
export function getAtomicRadii(model: Model): Float32Array {
|
||||
if (model._dynamicPropertyData[AtomicRadiiProp]) return model._dynamicPropertyData[AtomicRadiiProp];
|
||||
const nAtoms = model.atomicHierarchy.atoms._rowCount;
|
||||
const type_symbol = model.atomicHierarchy.atoms.type_symbol.value;
|
||||
const radii = new Float32Array(nAtoms);
|
||||
for (let i = 0; i < nAtoms; i++) {
|
||||
radii[i] = VdwRadius(type_symbol(i));
|
||||
}
|
||||
model._dynamicPropertyData[AtomicRadiiProp] = radii;
|
||||
return radii;
|
||||
}
|
||||
|
||||
function invertIndex(xs: Column<number>) {
|
||||
const invertedIndex = new Int32Array(xs.rowCount);
|
||||
let isIdentity = false;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2025s 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>
|
||||
@@ -98,6 +98,9 @@ export const UnknownSaccharideComponent: SaccharideComponent = {
|
||||
type: SaccharideType.Unknown
|
||||
};
|
||||
|
||||
/**
|
||||
* Unless stated otherwise, these follow SNFG recommendations.
|
||||
*/
|
||||
const Monosaccharides: SaccharideComponent[] = [
|
||||
{ abbr: 'Glc', name: 'Glucose', color: SaccharideColors.Blue, type: SaccharideType.Hexose },
|
||||
{ abbr: 'Man', name: 'Mannose', color: SaccharideColors.Green, type: SaccharideType.Hexose },
|
||||
@@ -125,7 +128,7 @@ const Monosaccharides: SaccharideComponent[] = [
|
||||
{ abbr: 'AllN', name: 'Allosamine', color: SaccharideColors.Purple, type: SaccharideType.Hexosamine },
|
||||
{ abbr: 'TalN', name: 'Talosamine', color: SaccharideColors.LightBlue, type: SaccharideType.Hexosamine },
|
||||
{ abbr: 'IdoN', name: 'Idosamine', color: SaccharideColors.Brown, type: SaccharideType.Hexosamine },
|
||||
{ abbr: 'GlcNS', name: 'N-sulfo Glucosamine', color: SaccharideColors.Blue, type: SaccharideType.Hexosamine },
|
||||
{ abbr: 'GlcNS', name: 'N-sulfo Glucosamine', color: SaccharideColors.Blue, type: SaccharideType.Hexosamine }, // from GLYCAM
|
||||
|
||||
{ abbr: 'GlcA', name: 'Glucuronic Acid', color: SaccharideColors.Blue, type: SaccharideType.Hexuronate },
|
||||
{ abbr: 'ManA', name: 'Mannuronic Acid', color: SaccharideColors.Green, type: SaccharideType.Hexuronate },
|
||||
@@ -148,7 +151,7 @@ const Monosaccharides: SaccharideComponent[] = [
|
||||
{ abbr: '6dAltNAc', name: 'N-Acetyl 6-Deoxy Altrosamine', color: SaccharideColors.Pink, type: SaccharideType.DeoxyhexNAc },
|
||||
{ abbr: '6dTalNAc', name: 'N-Acetyl 6-Deoxy Talosamine', color: SaccharideColors.LightBlue, type: SaccharideType.DeoxyhexNAc },
|
||||
{ abbr: 'FucNAc', name: 'N-Acetyl Fucosamine', color: SaccharideColors.Red, type: SaccharideType.DeoxyhexNAc },
|
||||
{ abbr: 'AAT', name: 'AAT', color: SaccharideColors.Red, type: SaccharideType.DeoxyhexNAc },
|
||||
{ abbr: 'AAT', name: 'AAT', color: SaccharideColors.Red, type: SaccharideType.DeoxyhexNAc }, // from GLYCAM
|
||||
|
||||
{ abbr: 'Oli', name: 'Olivose', color: SaccharideColors.Blue, type: SaccharideType.DiDeoxyhexose },
|
||||
{ abbr: 'Tyv', name: 'Tyvelose', color: SaccharideColors.Green, type: SaccharideType.DiDeoxyhexose },
|
||||
@@ -181,8 +184,8 @@ const Monosaccharides: SaccharideComponent[] = [
|
||||
{ abbr: 'MurNAc', name: 'N-Acetyl Muramic Acid', color: SaccharideColors.Purple, type: SaccharideType.Unknown },
|
||||
{ abbr: 'MurNGc', name: 'N-Glycolyl Muramic Acid', color: SaccharideColors.LightBlue, type: SaccharideType.Unknown },
|
||||
{ abbr: 'Mur', name: 'Muramic Acid', color: SaccharideColors.Brown, type: SaccharideType.Unknown },
|
||||
{ abbr: 'K3O', name: 'D-glycero-a-D-talo-oct-2-Ulosonic Acid', color: SaccharideColors.Red, type: SaccharideType.Unknown },
|
||||
{ abbr: 'dUA', name: '4-deoxy-4,5-didehydro iduronic acid', color: SaccharideColors.Secondary, type: SaccharideType.Unknown },
|
||||
{ abbr: 'K3O', name: 'D-glycero-a-D-talo-oct-2-Ulosonic Acid', color: SaccharideColors.Red, type: SaccharideType.Unknown }, // from GLYCAM
|
||||
{ abbr: 'dUA', name: '4-deoxy-4,5-didehydro iduronic acid', color: SaccharideColors.Secondary, type: SaccharideType.Unknown }, // from GLYCAM
|
||||
|
||||
{ abbr: 'Api', name: 'Apicose', color: SaccharideColors.Green, type: SaccharideType.Assigned },
|
||||
{ abbr: 'Fru', name: 'Fructose', color: SaccharideColors.Green, type: SaccharideType.Assigned },
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Adam Midlik <midlik@gmail.com>
|
||||
*/
|
||||
|
||||
import { SymmetryOperator } from '../../../mol-math/geometry/symmetry-operator';
|
||||
import { Model } from '../model';
|
||||
import { GridLookup3D, Lookup3D, Spacegroup } from '../../../mol-math/geometry';
|
||||
import { IntraUnitBonds, computeIntraUnitBonds } from './unit/bonds';
|
||||
import { VdwRadius } from '../model/properties/atomic';
|
||||
import { CoarseElements, CoarseSphereConformation, CoarseGaussianConformation } from '../model/properties/coarse';
|
||||
import { BitFlags } from '../../../mol-util';
|
||||
import { UnitRings } from './unit/rings';
|
||||
@@ -46,7 +48,7 @@ namespace Unit {
|
||||
|
||||
export function create<K extends Kind>(id: number, invariantId: number, chainGroupId: number, traits: Traits, kind: Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set, props?: K extends Kind.Atomic ? AtomicProperties : CoarseProperties): Unit {
|
||||
switch (kind) {
|
||||
case Kind.Atomic: return new Atomic(id, invariantId, chainGroupId, traits, model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation), props ?? AtomicProperties());
|
||||
case Kind.Atomic: return new Atomic(id, invariantId, chainGroupId, traits, model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation, getAtomicRadiusFunc(model)), props ?? AtomicProperties());
|
||||
case Kind.Spheres: return createCoarse(id, invariantId, chainGroupId, traits, model, Kind.Spheres, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.spheres, getSphereRadiusFunc(model)), props ?? CoarseProperties());
|
||||
case Kind.Gaussians: return createCoarse(id, invariantId, chainGroupId, traits, model, Kind.Gaussians, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.gaussians, getGaussianRadiusFunc(model)), props ?? CoarseProperties());
|
||||
}
|
||||
@@ -182,6 +184,11 @@ namespace Unit {
|
||||
return {};
|
||||
}
|
||||
|
||||
function getAtomicRadiusFunc(model: Model) {
|
||||
const type_symbol = model.atomicHierarchy.atoms.type_symbol.value;
|
||||
return (i: ElementIndex) => VdwRadius(type_symbol(i));
|
||||
}
|
||||
|
||||
function getSphereRadiusFunc(model: Model) {
|
||||
const r = model.coarseConformation.spheres.radius;
|
||||
return (i: ElementIndex) => r[i];
|
||||
@@ -270,7 +277,7 @@ namespace Unit {
|
||||
}
|
||||
|
||||
const conformation = (this.model.atomicConformation !== model.atomicConformation || operator !== this.conformation.operator)
|
||||
? SymmetryOperator.createMapping<ElementIndex>(operator, model.atomicConformation)
|
||||
? SymmetryOperator.createMapping<ElementIndex>(operator, model.atomicConformation, getAtomicRadiusFunc(model))
|
||||
: this.conformation;
|
||||
return new Atomic(this.id, this.invariantId, this.chainGroupId, this.traits, model, this.elements, conformation, props);
|
||||
}
|
||||
@@ -278,9 +285,10 @@ namespace Unit {
|
||||
get boundary() {
|
||||
if (this.props.boundary) return this.props.boundary;
|
||||
const { x, y, z } = this.model.atomicConformation;
|
||||
const radius = Model.getAtomicRadii(this.model);
|
||||
this.props.boundary = Traits.is(this.traits, Trait.FastBoundary)
|
||||
? getFastBoundary({ x, y, z, indices: this.elements })
|
||||
: getBoundary({ x, y, z, indices: this.elements });
|
||||
? getFastBoundary({ x, y, z, radius, indices: this.elements })
|
||||
: getBoundary({ x, y, z, radius, indices: this.elements });
|
||||
return this.props.boundary;
|
||||
}
|
||||
|
||||
@@ -456,11 +464,11 @@ namespace Unit {
|
||||
|
||||
get boundary() {
|
||||
if (this.props.boundary) return this.props.boundary;
|
||||
// TODO: support sphere radius?
|
||||
const { x, y, z } = this.getCoarseConformation();
|
||||
const radius = this.getCoarseRadii();
|
||||
this.props.boundary = Traits.is(this.traits, Trait.FastBoundary)
|
||||
? getFastBoundary({ x, y, z, indices: this.elements })
|
||||
: getBoundary({ x, y, z, indices: this.elements });
|
||||
? getFastBoundary({ x, y, z, radius, indices: this.elements })
|
||||
: getBoundary({ x, y, z, radius, indices: this.elements });
|
||||
return this.props.boundary;
|
||||
}
|
||||
|
||||
@@ -494,6 +502,10 @@ namespace Unit {
|
||||
return getCoarseConformation(this.kind, this.model);
|
||||
}
|
||||
|
||||
private getCoarseRadii() {
|
||||
return getCoarseRadii(this.kind, this.model);
|
||||
}
|
||||
|
||||
constructor(id: number, invariantId: number, chainGroupId: number, traits: Traits, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: CoarseProperties) {
|
||||
this.kind = kind;
|
||||
this.objectPrimitive = kind === Kind.Spheres ? 'sphere' : 'gaussian';
|
||||
@@ -514,6 +526,10 @@ namespace Unit {
|
||||
return kind === Kind.Spheres ? model.coarseConformation.spheres : model.coarseConformation.gaussians;
|
||||
}
|
||||
|
||||
function getCoarseRadii(kind: Kind, model: Model) {
|
||||
return kind === Kind.Spheres ? model.coarseConformation.spheres.radius : undefined; // Zero radius for gaussians
|
||||
}
|
||||
|
||||
interface CoarseProperties extends BaseProperties { }
|
||||
|
||||
function CoarseProperties(): CoarseProperties {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Grid {
|
||||
|
||||
export type Transform = { kind: 'spacegroup', cell: SpacegroupCell, fractionalBox: Box3D } | { kind: 'matrix', matrix: Mat4 }
|
||||
|
||||
const _scale = Mat4.zero(), _translate = Mat4.zero();
|
||||
const _scale = Mat4(), _translate = Mat4();
|
||||
export function getGridToCartesianTransform(grid: Grid) {
|
||||
if (grid.transform.kind === 'matrix') {
|
||||
return Mat4.copy(Mat4(), grid.transform.matrix);
|
||||
@@ -39,9 +39,9 @@ namespace Grid {
|
||||
|
||||
if (grid.transform.kind === 'spacegroup') {
|
||||
const { cells: { space } } = grid;
|
||||
const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(Vec3.zero(), grid.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
|
||||
const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3(), Box3D.size(Vec3(), grid.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
|
||||
const translate = Mat4.fromTranslation(_translate, grid.transform.fractionalBox.min);
|
||||
return Mat4.mul3(Mat4.zero(), grid.transform.cell.fromFractional, translate, scale);
|
||||
return Mat4.mul3(Mat4(), grid.transform.cell.fromFractional, translate, scale);
|
||||
}
|
||||
|
||||
return Mat4.identity();
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface Volume {
|
||||
transform: Mat4
|
||||
}>
|
||||
readonly sourceData: ModelFormat
|
||||
readonly periodicity?: 'none' | 'xyz'
|
||||
|
||||
// TODO use...
|
||||
customProperties: CustomProperties
|
||||
|
||||
@@ -4,23 +4,30 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Canvas3DParams } from '../../../mol-canvas3d/canvas3d';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { PluginState } from '../../../mol-plugin/state';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { PluginStateSnapshotManager } from '../../manager/snapshots';
|
||||
import { PluginStateAnimation } from '../model';
|
||||
|
||||
|
||||
async function setPartialSnapshot(plugin: PluginContext, entry: Partial<PluginStateSnapshotManager.Entry['snapshot']>, first = false) {
|
||||
if (entry.data) {
|
||||
await plugin.runTask(plugin.state.data.setSnapshot(entry.data));
|
||||
// update the canvas3d trackball with the snapshot
|
||||
if (entry.canvas3d?.props?.trackball) {
|
||||
plugin.canvas3d?.setProps({
|
||||
trackball: entry.canvas3d?.props?.trackball
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (entry.canvas3d?.props) {
|
||||
const settings = PD.normalizeParams(Canvas3DParams, entry.canvas3d.props, 'children');
|
||||
if (entry.camera?.current || entry.camera?.focus) {
|
||||
// Avoid multiple camera transitions (creates ugly cases when camera in old and new snapshot is the same)
|
||||
settings.camera = undefined;
|
||||
settings.cameraClipping = undefined;
|
||||
settings.cameraFog = undefined;
|
||||
}
|
||||
plugin.canvas3d?.setProps(settings);
|
||||
}
|
||||
if (entry.camera?.current) {
|
||||
plugin.canvas3d?.requestCameraReset({
|
||||
snapshot: entry.camera.current,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 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>
|
||||
*/
|
||||
|
||||
import type { EncodedFile } from '../../mol-io/common/binary-cif';
|
||||
import { decodeMsgPack } from '../../mol-io/common/msgpack/decode';
|
||||
import { StringLike } from '../../mol-io/common/string-like';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
@@ -31,11 +32,16 @@ export function guessCifVariant(info: FileNameInfo, data: Uint8Array | StringLik
|
||||
if (info.ext === 'bcif') {
|
||||
try {
|
||||
// TODO: find a way to run msgpackDecode only once
|
||||
// now it is run twice, here and during file parsing
|
||||
const { encoder } = decodeMsgPack(data as Uint8Array);
|
||||
if (encoder.startsWith('VolumeServer')) return 'dscif';
|
||||
// TODO: assumes volseg-volume-server only serves segments
|
||||
if (encoder.startsWith('volseg-volume-server')) return 'segcif';
|
||||
// now it is run twice, here and during file parsing
|
||||
const file = decodeMsgPack(data as Uint8Array) as EncodedFile;
|
||||
if (file.encoder.startsWith('VolumeServer')) return 'dscif';
|
||||
// Assumes volseg-volume-server only serves segments
|
||||
if (file.encoder.startsWith('volseg-volume-server')) return 'segcif';
|
||||
|
||||
if (bcifHasCategory(file, 'volume_data_3d_info')) {
|
||||
if (bcifHasCategory(file, 'volume_data_3d')) return 'dscif';
|
||||
if (bcifHasCategory(file, 'segmentation_data_3d')) return 'segcif';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -43,7 +49,26 @@ export function guessCifVariant(info: FileNameInfo, data: Uint8Array | StringLik
|
||||
const str = data as StringLike;
|
||||
if (str.startsWith('data_SERVER\n#\n_density_server_result')) return 'dscif';
|
||||
if (str.startsWith('data_SERVER\n#\ndata_SEGMENTATION_DATA')) return 'segcif';
|
||||
|
||||
if (cifHasCategory(str, 'volume_data_3d_info')) {
|
||||
if (cifHasCategory(str, 'volume_data_3d')) return 'dscif';
|
||||
if (cifHasCategory(str, 'segmentation_data_3d')) return 'segcif';
|
||||
}
|
||||
|
||||
if (str.includes('atom_site_fract_x') || str.includes('atom_site.fract_x')) return 'coreCif';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function cifHasCategory(file: StringLike, categoryName: string): boolean {
|
||||
return file.includes(`_${categoryName}.`);
|
||||
}
|
||||
|
||||
function bcifHasCategory(file: EncodedFile, categoryName: string): boolean {
|
||||
for (const block of file.dataBlocks) {
|
||||
for (const category of block.categories) {
|
||||
if (category.name === categoryName) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2025 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>
|
||||
@@ -25,7 +25,7 @@ import { changeCameraRotation, structureLayingTransform } from './focus-camera/o
|
||||
|
||||
// TODO: make this customizable somewhere?
|
||||
const DefaultCameraFocusOptions = {
|
||||
minRadius: 5,
|
||||
minRadius: 1,
|
||||
extraRadius: 4,
|
||||
durationMs: 250,
|
||||
};
|
||||
|
||||
@@ -195,6 +195,8 @@ const _Warning = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M1
|
||||
export function WarningSvg() { return _Warning; }
|
||||
const _ContentCut = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M 9.64 7.64 c 0.23 -0.5 0.36 -1.05 0.36 -1.64 c 0 -2.21 -1.79 -4 -4 -4 S 2 3.79 2 6 s 1.79 4 4 4 c 0.59 0 1.14 -0.13 1.64 -0.36 L 10 12 l -2.36 2.36 C 7.14 14.13 6.59 14 6 14 c -2.21 0 -4 1.79 -4 4 s 1.79 4 4 4 s 4 -1.79 4 -4 c 0 -0.59 -0.13 -1.14 -0.36 -1.64 L 12 14 l 7 7 h 3 v -1 L 9.64 7.64 Z M 6 8 c -1.1 0 -2 -0.89 -2 -2 s 0.9 -2 2 -2 s 2 0.89 2 2 s -0.9 2 -2 2 Z m 0 12 c -1.1 0 -2 -0.89 -2 -2 s 0.9 -2 2 -2 s 2 0.89 2 2 s -0.9 2 -2 2 Z m 6 -7.5 c -0.28 0 -0.5 -0.22 -0.5 -0.5 s 0.22 -0.5 0.5 -0.5 s 0.5 0.22 0.5 0.5 s -0.22 0.5 -0.5 0.5 Z M 19 3 l -6 6 l 2 2 l 7 -7 V 3 Z' /></svg>;
|
||||
export function ContentCutSvg() { return _ContentCut; }
|
||||
const _LightMode = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0 c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2 c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1 C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06 c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41 l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41 c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36 c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z' /></svg>;
|
||||
export function LightModeSvg() { return _LightMode; }
|
||||
|
||||
// The following icons are adapted from https://icons.getbootstrap.com/ and
|
||||
// licensed with https://github.com/twbs/bootstrap/blob/main/LICENSE
|
||||
|
||||
@@ -418,12 +418,19 @@ export class TextControl extends SimpleParam<PD.Text> {
|
||||
|
||||
function TextCtrl({ props, placeholder, update }: { props: ParamProps<PD.Text>, placeholder: string, update: (v: string) => any }) {
|
||||
const [value, setValue] = React.useState(props.value);
|
||||
const [composition, setComposition] = React.useState(false);
|
||||
React.useEffect(() => setValue(props.value), [props.value]);
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
if (props.param.disableInteractiveUpdates) setValue(e.target.value);
|
||||
if (composition || props.param.disableInteractiveUpdates) setValue(e.target.value);
|
||||
else update(e.target.value);
|
||||
};
|
||||
const onCompositionEnd = (e: React.CompositionEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setComposition(false);
|
||||
const target = e.target as EventTarget & (HTMLInputElement | HTMLTextAreaElement);
|
||||
if (props.param.disableInteractiveUpdates) setValue(target.value);
|
||||
else update(target.value);
|
||||
};
|
||||
const onBlur = (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
if (props.param.disableInteractiveUpdates) update(e.target.value);
|
||||
};
|
||||
@@ -443,13 +450,15 @@ function TextCtrl({ props, placeholder, update }: { props: ParamProps<PD.Text>,
|
||||
return <div className='msp-control-text-area-wrapper'>
|
||||
<textarea
|
||||
value={value ?? ''} placeholder={placeholder} disabled={props.isDisabled}
|
||||
onChange={onChange} onBlur={onBlur} onKeyDown={onKeyDown}
|
||||
onChange={onChange} onBlur={onBlur} onKeyDown={onKeyDown}
|
||||
onCompositionStart={() => setComposition(true)} onCompositionEnd={onCompositionEnd}
|
||||
/>
|
||||
</div>;
|
||||
} else {
|
||||
return <input type='text'
|
||||
value={value ?? ''} placeholder={placeholder} disabled={props.isDisabled}
|
||||
onChange={onChange} onBlur={onBlur} onKeyDown={onKeyDown}
|
||||
onChange={onChange} onBlur={onBlur} onKeyDown={onKeyDown}
|
||||
onCompositionStart={() => setComposition(true)} onCompositionEnd={onCompositionEnd}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2025 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>
|
||||
@@ -14,7 +14,7 @@ import { PluginConfig } from '../mol-plugin/config';
|
||||
import { ParamDefinition as PD } from '../mol-util/param-definition';
|
||||
import { PluginUIComponent } from './base';
|
||||
import { Button, ControlGroup, IconButton } from './controls/common';
|
||||
import { AspectRatioSvg, AutorenewSvg, BuildOutlinedSvg, CameraOutlinedSvg, CloseSvg, FullscreenSvg, HeadsetVRSvg, TuneSvg } from './controls/icons';
|
||||
import { AspectRatioSvg, AutorenewSvg, BuildOutlinedSvg, CameraOutlinedSvg, CloseSvg, FullscreenSvg, HeadsetVRSvg, LightModeSvg, TuneSvg } from './controls/icons';
|
||||
import { ToggleSelectionModeButton } from './structure/selection';
|
||||
import { ViewportCanvas } from './viewport/canvas';
|
||||
import { DownloadScreenshotControls } from './viewport/screenshot';
|
||||
@@ -85,6 +85,19 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
|
||||
}
|
||||
};
|
||||
|
||||
toggleIllumination = () => {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
settings: {
|
||||
illumination: {
|
||||
...this.plugin.canvas3d.props.illumination,
|
||||
enabled: !this.plugin.canvas3d.props.illumination.enabled
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
setSettings = (p: { param: PD.Base<any>, name: string, value: any }) => {
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { [p.name]: p.value } });
|
||||
};
|
||||
@@ -178,6 +191,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
|
||||
&& this.plugin.config.get(PluginConfig.Viewport.ShowToggleFullscreen)
|
||||
&& this.icon(AspectRatioSvg, this.toggleFullscreen, 'Toggle Full Screen', this.plugin.layout.state.expandToFullscreen)}
|
||||
{this.plugin.config.get(PluginConfig.Viewport.ShowSettings) && this.icon(TuneSvg, this.toggleSettingsExpanded, 'Settings / Controls Info', this.state.isSettingsExpanded)}
|
||||
{this.plugin.config.get(PluginConfig.Viewport.ShowIllumination) && this.icon(LightModeSvg, this.toggleIllumination, 'Illumination', this.plugin.canvas3d?.props.illumination.enabled || false)}
|
||||
{xr && this.icon(HeadsetVRSvg, this.toggleXR, xrTitle, xrIsPresenting, !xrIsSupported)}
|
||||
</div>
|
||||
{this.plugin.config.get(PluginConfig.Viewport.ShowSelectionMode) && <div>
|
||||
|
||||
@@ -190,13 +190,21 @@ export const CameraControls = PluginBehavior.create<CameraControlsProps>({
|
||||
if (Binding.matchKey(b.keySpinAnimation, code, modifiers, key)) {
|
||||
const name = tp.animate.name !== 'spin' ? 'spin' : 'off';
|
||||
if (name === 'off') {
|
||||
this.ctx.canvas3d.setProps({
|
||||
trackball: { animate: { name, params: {} } }
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
trackball: {
|
||||
...tp,
|
||||
animate: { name, params: {} }
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.ctx.canvas3d.setProps({
|
||||
trackball: { animate: {
|
||||
name, params: { speed: 1 } }
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
trackball: {
|
||||
...tp,
|
||||
animate: { name, params: { speed: 0.1 } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -205,13 +213,21 @@ export const CameraControls = PluginBehavior.create<CameraControlsProps>({
|
||||
if (Binding.matchKey(b.keyRockAnimation, code, modifiers, key)) {
|
||||
const name = tp.animate.name !== 'rock' ? 'rock' : 'off';
|
||||
if (name === 'off') {
|
||||
this.ctx.canvas3d.setProps({
|
||||
trackball: { animate: { name, params: {} } }
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
trackball: {
|
||||
...tp,
|
||||
animate: { name, params: {} }
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.ctx.canvas3d.setProps({
|
||||
trackball: { animate: {
|
||||
name, params: { speed: 0.3, angle: 10 } }
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
trackball: {
|
||||
...tp,
|
||||
animate: { name, params: { speed: 0.3, angle: 10 } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -220,8 +236,13 @@ export const CameraControls = PluginBehavior.create<CameraControlsProps>({
|
||||
if (Binding.matchKey(b.keyToggleFlyMode, code, modifiers, key)) {
|
||||
const flyMode = !tp.flyMode;
|
||||
|
||||
this.ctx.canvas3d.setProps({
|
||||
trackball: { flyMode }
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
trackball: {
|
||||
...tp,
|
||||
flyMode
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this.ctx.canvas3dContext?.canvas) {
|
||||
@@ -234,10 +255,12 @@ export const CameraControls = PluginBehavior.create<CameraControlsProps>({
|
||||
}
|
||||
|
||||
if (Binding.matchKey(b.keyGlobalIllumination, code, modifiers, key)) {
|
||||
this.ctx.canvas3d.setProps({
|
||||
illumination: {
|
||||
...ip,
|
||||
enabled: !ip.enabled,
|
||||
PluginCommands.Canvas3D.SetSettings(this.ctx, {
|
||||
settings: {
|
||||
illumination: {
|
||||
...ip,
|
||||
enabled: !ip.enabled
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ export const PluginConfig = {
|
||||
ShowAnimation: item('viewer.show-animation-button', true),
|
||||
ShowTrajectoryControls: item('viewer.show-trajectory-controls', true),
|
||||
ShowScreenshotControls: item('viewer.show-screenshot-controls', true),
|
||||
ShowIllumination: item('viewer.show-illumination-button', true),
|
||||
ShowXR: item<'auto' | 'always' | 'never'>('viewer.show-xr', 'always'),
|
||||
},
|
||||
Download: {
|
||||
|
||||
@@ -89,7 +89,13 @@ class PluginState extends PluginComponent {
|
||||
if (snapshot.behaviour) await this.plugin.runTask(this.behaviors.setSnapshot(snapshot.behaviour));
|
||||
if (snapshot.data) await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
|
||||
if (snapshot.canvas3d?.props) {
|
||||
const settings = PD.normalizeParams(Canvas3DParams, snapshot.canvas3d.props, 'children');
|
||||
const settings: Partial<Canvas3DProps> = PD.normalizeParams(Canvas3DParams, snapshot.canvas3d.props, 'children');
|
||||
if (snapshot.camera?.current || snapshot.camera?.focus) {
|
||||
// Avoid multiple camera transitions (creates ugly cases when camera in old and new snapshot is the same)
|
||||
settings.camera = undefined;
|
||||
settings.cameraClipping = undefined;
|
||||
settings.cameraFog = undefined;
|
||||
}
|
||||
await PluginCommands.Canvas3D.SetSettings(this.plugin, { settings });
|
||||
}
|
||||
if (snapshot.canvas3dContext?.props) {
|
||||
|
||||
@@ -247,7 +247,7 @@ function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, struct
|
||||
const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
|
||||
|
||||
const buffer = textureMesh?.doubleBuffer.get();
|
||||
const gv = extractIsosurface(webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
const gv = extractIsosurface(webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridDataDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
if (isTimingMode) webgl.timer.markEnd('createGaussianSurfaceTextureMesh');
|
||||
|
||||
const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, densityTextureData.maxRadius);
|
||||
@@ -333,7 +333,7 @@ function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, structure
|
||||
const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
|
||||
|
||||
const buffer = textureMesh?.doubleBuffer.get();
|
||||
const gv = extractIsosurface(webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
const gv = extractIsosurface(webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridDataDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, false, true, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
if (isTimingMode) webgl.timer.markEnd('createStructureGaussianSurfaceTextureMesh');
|
||||
|
||||
const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, densityTextureData.maxRadius);
|
||||
|
||||
@@ -21,13 +21,19 @@ import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { applyMeshColorSmoothing } from '../../../mol-geo/geometry/mesh/color-smoothing';
|
||||
import { ColorSmoothingParams, getColorSmoothingProps } from '../../../mol-geo/geometry/base';
|
||||
import { BaseGeometry, ColorSmoothingParams, getColorSmoothingProps } from '../../../mol-geo/geometry/base';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { ComplexMeshVisual, ComplexVisual } from '../complex-visual';
|
||||
|
||||
const CommonMolecularSurfaceCalculationParams = {
|
||||
...MolecularSurfaceCalculationParams,
|
||||
resolution: { ...MolecularSurfaceCalculationParams.resolution, ...BaseGeometry.CustomQualityParamInfo },
|
||||
probePositions: { ...MolecularSurfaceCalculationParams.probePositions, ...BaseGeometry.CustomQualityParamInfo },
|
||||
};
|
||||
|
||||
export const MolecularSurfaceMeshParams = {
|
||||
...UnitsMeshParams,
|
||||
...MolecularSurfaceCalculationParams,
|
||||
...CommonMolecularSurfaceCalculationParams,
|
||||
...CommonSurfaceParams,
|
||||
...ColorSmoothingParams,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
import { Segmentation } from '../../../../../mol-data/int';
|
||||
import { Segmentation } from '../../../../../mol-data/int/segmentation';
|
||||
import { SortedRanges } from '../../../../../mol-data/int/sorted-ranges';
|
||||
import { ElementIndex, ResidueIndex, Unit } from '../../../../../mol-model/structure';
|
||||
import { MoleculeType } from '../../../../../mol-model/structure/model/types';
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import { Unit, StructureElement, ElementIndex, ResidueIndex, Structure } from '../../../../../mol-model/structure';
|
||||
import { Segmentation, SortedArray } from '../../../../../mol-data/int';
|
||||
import { MoleculeType, SecondaryStructureType } from '../../../../../mol-model/structure/model/types';
|
||||
import { Iterator } from '../../../../../mol-data/iterator';
|
||||
import { Vec3 } from '../../../../../mol-math/linear-algebra';
|
||||
@@ -17,6 +16,8 @@ import { AtomicConformation } from '../../../../../mol-model/structure/model/pro
|
||||
import { SecondaryStructureProvider } from '../../../../../mol-model-props/computed/secondary-structure';
|
||||
import { HelixOrientationProvider } from '../../../../../mol-model-props/computed/helix-orientation';
|
||||
import { SecondaryStructure } from '../../../../../mol-model/structure/model/properties/secondary-structure';
|
||||
import { Segmentation } from '../../../../../mol-data/int/segmentation';
|
||||
import { SortedArray } from '../../../../../mol-data/int/sorted-array';
|
||||
|
||||
function isHelixSS(ss: SecondaryStructureType.Flag) {
|
||||
return SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Helix);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Box3D, SpacegroupCell } from '../mol-math/geometry';
|
||||
import { ModelSymmetry } from '../mol-model-formats/structure/property/symmetry';
|
||||
import { Volume } from '../mol-model/volume';
|
||||
import { Location } from '../mol-model/location';
|
||||
import { isStandaloneHmd } from '../mol-util/browser';
|
||||
|
||||
export interface VisualUpdateState {
|
||||
updateTransform: boolean
|
||||
@@ -76,6 +77,28 @@ export const DefaultQualityThresholds = {
|
||||
};
|
||||
export type QualityThresholds = typeof DefaultQualityThresholds
|
||||
|
||||
enum QualityLevel {
|
||||
Lowest,
|
||||
Lower,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Higher,
|
||||
Highest
|
||||
}
|
||||
|
||||
function visualQualityToLevel(quality: Exclude<VisualQuality, 'auto' | 'custom'>): QualityLevel {
|
||||
switch (quality) {
|
||||
case 'lowest': return QualityLevel.Lowest;
|
||||
case 'lower': return QualityLevel.Lower;
|
||||
case 'low': return QualityLevel.Low;
|
||||
case 'medium': return QualityLevel.Medium;
|
||||
case 'high': return QualityLevel.High;
|
||||
case 'higher': return QualityLevel.Higher;
|
||||
case 'highest': return QualityLevel.Highest;
|
||||
}
|
||||
}
|
||||
|
||||
export function getStructureQuality(structure: Structure, tresholds: Partial<QualityThresholds> = {}): VisualQuality {
|
||||
const t = { ...DefaultQualityThresholds, ...tresholds };
|
||||
let score = structure.elementCount * t.elementCountFactor;
|
||||
@@ -132,73 +155,77 @@ export function getQualityProps(props: Partial<QualityProps>, data?: any) {
|
||||
}
|
||||
}
|
||||
|
||||
switch (quality) {
|
||||
case 'highest':
|
||||
detail = 3;
|
||||
radialSegments = 36;
|
||||
linearSegments = 18;
|
||||
resolution = 0.1;
|
||||
imageResolution = 0.01;
|
||||
probePositions = 72;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case 'higher':
|
||||
detail = 3;
|
||||
radialSegments = 28;
|
||||
linearSegments = 14;
|
||||
resolution = 0.3;
|
||||
imageResolution = 0.05;
|
||||
probePositions = 48;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case 'high':
|
||||
detail = 2;
|
||||
radialSegments = 20;
|
||||
linearSegments = 10;
|
||||
resolution = 0.5;
|
||||
imageResolution = 0.1;
|
||||
probePositions = 36;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case 'medium':
|
||||
detail = 1;
|
||||
radialSegments = 12;
|
||||
linearSegments = 8;
|
||||
resolution = 0.8;
|
||||
imageResolution = 0.2;
|
||||
probePositions = 24;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case 'low':
|
||||
detail = 0;
|
||||
radialSegments = 8;
|
||||
linearSegments = 3;
|
||||
resolution = 1.3;
|
||||
imageResolution = 0.4;
|
||||
probePositions = 24;
|
||||
doubleSided = false;
|
||||
break;
|
||||
case 'lower':
|
||||
detail = 0;
|
||||
radialSegments = 4;
|
||||
linearSegments = 2;
|
||||
resolution = 3;
|
||||
imageResolution = 0.7;
|
||||
probePositions = 12;
|
||||
doubleSided = false;
|
||||
break;
|
||||
case 'lowest':
|
||||
detail = 0;
|
||||
radialSegments = 2;
|
||||
linearSegments = 1;
|
||||
resolution = 8;
|
||||
imageResolution = 1;
|
||||
probePositions = 12;
|
||||
doubleSided = false;
|
||||
break;
|
||||
case 'custom':
|
||||
// use defaults or given props as set above
|
||||
break;
|
||||
if (quality !== 'custom' && quality !== 'auto') {
|
||||
let level = visualQualityToLevel(quality);
|
||||
if (isStandaloneHmd()) {
|
||||
level = Math.max(level - 1, QualityLevel.Lowest);
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case QualityLevel.Highest:
|
||||
detail = 3;
|
||||
radialSegments = 36;
|
||||
linearSegments = 18;
|
||||
resolution = 0.1;
|
||||
imageResolution = 0.01;
|
||||
probePositions = 72;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case QualityLevel.Higher:
|
||||
detail = 3;
|
||||
radialSegments = 28;
|
||||
linearSegments = 14;
|
||||
resolution = 0.3;
|
||||
imageResolution = 0.05;
|
||||
probePositions = 48;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case QualityLevel.High:
|
||||
detail = 2;
|
||||
radialSegments = 20;
|
||||
linearSegments = 10;
|
||||
resolution = 0.5;
|
||||
imageResolution = 0.1;
|
||||
probePositions = 36;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case QualityLevel.Medium:
|
||||
detail = 1;
|
||||
radialSegments = 12;
|
||||
linearSegments = 8;
|
||||
resolution = 0.8;
|
||||
imageResolution = 0.2;
|
||||
probePositions = 24;
|
||||
doubleSided = true;
|
||||
break;
|
||||
case QualityLevel.Low:
|
||||
detail = 0;
|
||||
radialSegments = 8;
|
||||
linearSegments = 3;
|
||||
resolution = 1.3;
|
||||
imageResolution = 0.4;
|
||||
probePositions = 24;
|
||||
doubleSided = false;
|
||||
break;
|
||||
case QualityLevel.Lower:
|
||||
detail = 0;
|
||||
radialSegments = 4;
|
||||
linearSegments = 2;
|
||||
resolution = 3;
|
||||
imageResolution = 0.7;
|
||||
probePositions = 12;
|
||||
doubleSided = false;
|
||||
break;
|
||||
case QualityLevel.Lowest:
|
||||
detail = 0;
|
||||
radialSegments = 2;
|
||||
linearSegments = 1;
|
||||
resolution = 8;
|
||||
imageResolution = 1;
|
||||
probePositions = 12;
|
||||
doubleSided = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// max resolution based on volume (for 'auto' quality)
|
||||
|
||||
@@ -18,7 +18,7 @@ import { calculateTransformBoundingSphere } from '../mol-gl/renderable/util';
|
||||
import { ValueCell } from '../mol-util';
|
||||
import { Overpaint } from '../mol-theme/overpaint';
|
||||
import { createOverpaint, clearOverpaint, applyOverpaintColor } from '../mol-geo/geometry/overpaint-data';
|
||||
import { Interval } from '../mol-data/int';
|
||||
import { Interval } from '../mol-data/int/interval';
|
||||
import { Transparency } from '../mol-theme/transparency';
|
||||
import { createTransparency, clearTransparency, applyTransparencyValue, getTransparencyAverage, getTransparencyMin } from '../mol-geo/geometry/transparency-data';
|
||||
import { Clipping } from '../mol-theme/clipping';
|
||||
|
||||
@@ -16,11 +16,12 @@ import { Theme, ThemeRegistryContext } from '../../mol-theme/theme';
|
||||
import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation';
|
||||
import { VisualUpdateState } from '../util';
|
||||
import { RepresentationContext, RepresentationParamsGetter } from '../representation';
|
||||
import { Interval, OrderedSet } from '../../mol-data/int';
|
||||
import { Loci, EmptyLoci } from '../../mol-model/loci';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { createVolumeCellLocationIterator, createVolumeTexture2d, createVolumeTexture3d, eachVolumeLoci, getVolumeTexture2dLayout } from './util';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Interval } from '../../mol-data/int/interval';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
|
||||
function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
|
||||
const bbox = Box3D();
|
||||
|
||||
@@ -15,7 +15,6 @@ import { VisualUpdateState } from '../util';
|
||||
import { RepresentationContext, RepresentationParamsGetter, Representation } from '../representation';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../mol-data/int';
|
||||
import { createVolumeCellLocationIterator, eachVolumeLoci } from './util';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { BaseGeometry } from '../../mol-geo/geometry/base';
|
||||
@@ -28,6 +27,8 @@ import { sphereVertexCount } from '../../mol-geo/primitive/sphere';
|
||||
import { Points } from '../../mol-geo/geometry/points/points';
|
||||
import { PointsBuilder } from '../../mol-geo/geometry/points/points-builder';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { Interval } from '../../mol-data/int/interval';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
|
||||
export const VolumeDotParams = {
|
||||
isoValue: Volume.IsoValueParam,
|
||||
|
||||
@@ -17,10 +17,9 @@ import { Lines } from '../../mol-geo/geometry/lines/lines';
|
||||
import { RepresentationContext, RepresentationParamsGetter, Representation } from '../representation';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../mol-data/int';
|
||||
import { Tensor, Vec2, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { createVolumeCellLocationIterator, createVolumeTexture2d, eachVolumeLoci, getVolumeTexture2dLayout } from './util';
|
||||
import { createVolumeCellLocationIterator, createVolumeTexture2d, createWrappedVolume, eachVolumeLoci, getVolumeTexture2dLayout } from './util';
|
||||
import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
|
||||
import { extractIsosurface } from '../../mol-gl/compute/marching-cubes/isosurface';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
@@ -28,25 +27,34 @@ import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { BaseGeometry } from '../../mol-geo/geometry/base';
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Interval } from '../../mol-data/int/interval';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
|
||||
export const VolumeIsosurfaceParams = {
|
||||
isoValue: Volume.IsoValueParam,
|
||||
wrap: PD.Select('auto', PD.arrayToOptions(['off', 'on', 'auto'] as const)),
|
||||
};
|
||||
export type VolumeIsosurfaceParams = typeof VolumeIsosurfaceParams
|
||||
export type VolumeIsosurfaceProps = PD.Values<VolumeIsosurfaceParams>
|
||||
|
||||
export const VolumeIsosurfaceTextureParams = {
|
||||
isoValue: Volume.IsoValueParam,
|
||||
...VolumeIsosurfaceParams,
|
||||
tryUseGpu: PD.Boolean(true),
|
||||
gpuDataType: PD.Select('byte', PD.arrayToOptions(['byte', 'float', 'halfFloat'] as const), { hideIf: p => !p.tryUseGpu }),
|
||||
};
|
||||
export type VolumeIsosurfaceGpuParams = typeof VolumeIsosurfaceTextureParams
|
||||
export type VolumeIsosurfaceGpuProps = PD.Values<VolumeIsosurfaceGpuParams>
|
||||
export type VolumeIsosurfaceTextureParams = typeof VolumeIsosurfaceTextureParams
|
||||
export type VolumeIsosurfaceTextureProps = PD.Values<VolumeIsosurfaceTextureParams>
|
||||
|
||||
function gpuSupport(webgl: WebGLContext) {
|
||||
return webgl.extensions.colorBufferFloat && webgl.extensions.textureFloat && webgl.extensions.drawBuffers;
|
||||
}
|
||||
|
||||
function shouldWrap(volume: Volume, wrap: VolumeIsosurfaceProps['wrap']) {
|
||||
if (wrap === 'on') return true;
|
||||
if (wrap === 'off') return false;
|
||||
return volume.periodicity === 'xyz';
|
||||
}
|
||||
|
||||
const Padding = 1;
|
||||
|
||||
function suitableForGpu(volume: Volume, webgl: WebGLContext) {
|
||||
@@ -97,12 +105,17 @@ export function eachIsosurface(loci: Loci, volume: Volume, key: number, props: V
|
||||
export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Volume, key: number, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) {
|
||||
ctx.runtime.update({ message: 'Marching cubes...' });
|
||||
|
||||
let cells = volume.grid.cells;
|
||||
if (shouldWrap(volume, props.wrap)) {
|
||||
cells = createWrappedVolume(volume).grid.cells;
|
||||
}
|
||||
|
||||
const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
|
||||
|
||||
const surface = await computeMarchingCubesMesh({
|
||||
isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
|
||||
scalarField: volume.grid.cells,
|
||||
idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
|
||||
scalarField: cells,
|
||||
idField: Tensor.create(cells.space, Tensor.Data1(ids))
|
||||
}, mesh).runAsChild(ctx.runtime);
|
||||
|
||||
const transform = Grid.getGridToCartesianTransform(volume.grid);
|
||||
@@ -138,7 +151,10 @@ export function IsosurfaceMeshVisual(materialId: number): VolumeVisual<Isosurfac
|
||||
getLoci: getIsosurfaceLoci,
|
||||
eachLocation: eachIsosurface,
|
||||
setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
|
||||
if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
|
||||
state.createGeometry = (
|
||||
!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats) ||
|
||||
newProps.wrap !== currentProps.wrap
|
||||
);
|
||||
},
|
||||
geometryUtils: Mesh.Utils,
|
||||
mustRecreate: (volumekey: VolumeKey, props: PD.Values<IsosurfaceMeshParams>, webgl?: WebGLContext) => {
|
||||
@@ -155,11 +171,15 @@ namespace VolumeIsosurfaceTexture {
|
||||
export function clear(volume: Volume) {
|
||||
delete volume._propertyData[name];
|
||||
}
|
||||
export function get(volume: Volume, webgl: WebGLContext, props: VolumeIsosurfaceGpuProps) {
|
||||
export function get(volume: Volume, webgl: WebGLContext, props: VolumeIsosurfaceTextureProps) {
|
||||
const { gpuDataType } = props;
|
||||
const wrap = shouldWrap(volume, props.wrap);
|
||||
|
||||
const transform = Grid.getGridToCartesianTransform(volume.grid);
|
||||
const gridDimension = Vec3.clone(volume.grid.cells.space.dimensions as Vec3);
|
||||
const { width, height, powerOfTwoSize: texDim } = getVolumeTexture2dLayout(gridDimension, Padding);
|
||||
const gridTexDim = Vec3.create(width, height, 0);
|
||||
const gridDataDim = Vec3.subScalar(Vec3(), gridDimension, wrap ? 1 : 0);
|
||||
const gridTexScale = Vec2.create(width / texDim, height / texDim);
|
||||
// console.log({ texDim, width, height, gridDimension });
|
||||
|
||||
@@ -167,15 +187,15 @@ namespace VolumeIsosurfaceTexture {
|
||||
throw new Error('volume too large for gpu isosurface extraction');
|
||||
}
|
||||
|
||||
const dataType = props.gpuDataType === 'halfFloat' && !webgl.extensions.textureHalfFloat ? 'float' : props.gpuDataType;
|
||||
const dataType = gpuDataType === 'halfFloat' && !webgl.extensions.textureHalfFloat ? 'float' : gpuDataType;
|
||||
|
||||
if (volume._propertyData[name]?.dataType !== dataType) {
|
||||
if (volume._propertyData[name]?.dataType !== dataType || volume._propertyData[name]?.wrap !== wrap) {
|
||||
const texture = dataType === 'byte'
|
||||
? webgl.resources.texture('image-uint8', 'alpha', 'ubyte', 'linear')
|
||||
: dataType === 'halfFloat'
|
||||
? webgl.resources.texture('image-float16', 'alpha', 'fp16', 'linear')
|
||||
: webgl.resources.texture('image-float32', 'alpha', 'float', 'linear');
|
||||
volume._propertyData[name] = { texture, dataType };
|
||||
volume._propertyData[name] = { texture, dataType, wrap };
|
||||
texture.define(texDim, texDim);
|
||||
// load volume into sub-section of texture
|
||||
texture.load(createVolumeTexture2d(volume, 'data', Padding, dataType), true);
|
||||
@@ -191,12 +211,13 @@ namespace VolumeIsosurfaceTexture {
|
||||
transform,
|
||||
gridDimension,
|
||||
gridTexDim,
|
||||
gridDataDim,
|
||||
gridTexScale
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Volume, key: number, theme: Theme, props: VolumeIsosurfaceGpuProps, textureMesh?: TextureMesh) {
|
||||
function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Volume, key: number, theme: Theme, props: VolumeIsosurfaceTextureProps, textureMesh?: TextureMesh) {
|
||||
const { webgl } = ctx;
|
||||
if (!webgl) throw new Error('webgl context required to create volume isosurface texture-mesh');
|
||||
|
||||
@@ -204,6 +225,10 @@ function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Volume, k
|
||||
return TextureMesh.createEmpty(textureMesh);
|
||||
}
|
||||
|
||||
if (shouldWrap(volume, props.wrap)) {
|
||||
volume = createWrappedVolume(volume);
|
||||
}
|
||||
|
||||
const { max, min } = volume.grid.stats;
|
||||
const diff = max - min;
|
||||
const value = Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue;
|
||||
@@ -214,10 +239,10 @@ function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Volume, k
|
||||
const boundingSphere = Volume.getBoundingSphere(volume); // getting isosurface bounding-sphere is too expensive here
|
||||
|
||||
const create = (textureMesh?: TextureMesh) => {
|
||||
const { texture, gridDimension, gridTexDim, gridTexScale, transform } = VolumeIsosurfaceTexture.get(volume, webgl, props);
|
||||
const { texture, gridDimension, gridTexDim, gridDataDim, gridTexScale, transform } = VolumeIsosurfaceTexture.get(volume, webgl, props);
|
||||
|
||||
const buffer = textureMesh?.doubleBuffer.get();
|
||||
const gv = extractIsosurface(webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, value < 0, false, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
const gv = extractIsosurface(webgl, texture, gridDimension, gridTexDim, gridDataDim, gridTexScale, transform, isoLevel, value < 0, false, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
|
||||
return TextureMesh.create(gv.vertexCount, groupCount, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh);
|
||||
};
|
||||
@@ -240,8 +265,11 @@ export function IsosurfaceTextureMeshVisual(materialId: number): VolumeVisual<Is
|
||||
getLoci: getIsosurfaceLoci,
|
||||
eachLocation: eachIsosurface,
|
||||
setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
|
||||
if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
|
||||
if (newProps.gpuDataType !== currentProps.gpuDataType) state.createGeometry = true;
|
||||
state.createGeometry = (
|
||||
!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats) ||
|
||||
newProps.gpuDataType !== currentProps.gpuDataType ||
|
||||
newProps.wrap !== currentProps.wrap
|
||||
);
|
||||
},
|
||||
geometryUtils: TextureMesh.Utils,
|
||||
mustRecreate: (volumeKey: VolumeKey, props: PD.Values<IsosurfaceMeshParams>, webgl?: WebGLContext) => {
|
||||
@@ -261,12 +289,17 @@ export function IsosurfaceTextureMeshVisual(materialId: number): VolumeVisual<Is
|
||||
export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: Volume, key: number, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) {
|
||||
ctx.runtime.update({ message: 'Marching cubes...' });
|
||||
|
||||
let cells = volume.grid.cells;
|
||||
if (shouldWrap(volume, props.wrap)) {
|
||||
cells = createWrappedVolume(volume).grid.cells;
|
||||
}
|
||||
|
||||
const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
|
||||
|
||||
const wireframe = await computeMarchingCubesLines({
|
||||
isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
|
||||
scalarField: volume.grid.cells,
|
||||
idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
|
||||
scalarField: cells,
|
||||
idField: Tensor.create(cells.space, Tensor.Data1(ids))
|
||||
}, lines).runAsChild(ctx.runtime);
|
||||
|
||||
const transform = Grid.getGridToCartesianTransform(volume.grid);
|
||||
@@ -293,7 +326,10 @@ export function IsosurfaceWireframeVisual(materialId: number): VolumeVisual<Isos
|
||||
getLoci: getIsosurfaceLoci,
|
||||
eachLocation: eachIsosurface,
|
||||
setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => {
|
||||
if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
|
||||
state.createGeometry = (
|
||||
!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats) ||
|
||||
newProps.wrap !== currentProps.wrap
|
||||
);
|
||||
},
|
||||
geometryUtils: Lines.Utils
|
||||
}, materialId);
|
||||
|
||||
@@ -14,7 +14,6 @@ import { createTransform, TransformData } from '../../mol-geo/geometry/transform
|
||||
import { createRenderObject, getNextMaterialId, GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { Loci, isEveryLoci, EmptyLoci, isEmptyLoci } from '../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../mol-data/int';
|
||||
import { getQualityProps, LocationCallback, VisualUpdateState } from '../util';
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
@@ -37,6 +36,8 @@ import { createMarkers } from '../../mol-geo/geometry/marker-data';
|
||||
import { Emissive } from '../../mol-theme/emissive';
|
||||
import { SizeTheme } from '../../mol-theme/size';
|
||||
import { Sphere3D } from '../../mol-math/geometry/primitives/sphere3d';
|
||||
import { Interval } from '../../mol-data/int/interval';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
|
||||
export type VolumeKey = { volume: Volume, key: number }
|
||||
export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeKey, P> { }
|
||||
|
||||
@@ -16,7 +16,6 @@ import { VisualUpdateState } from '../util';
|
||||
import { RepresentationContext, RepresentationParamsGetter, Representation } from '../representation';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { Interval, OrderedSet, SortedArray } from '../../mol-data/int';
|
||||
import { Mat4, Tensor, Vec2, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { createSegmentTexture2d, eachVolumeLoci, getVolumeTexture2dLayout } from './util';
|
||||
@@ -26,6 +25,9 @@ import { BaseGeometry } from '../../mol-geo/geometry/base';
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { extractIsosurface } from '../../mol-gl/compute/marching-cubes/isosurface';
|
||||
import { Box3D } from '../../mol-math/geometry/primitives/box3d';
|
||||
import { SortedArray } from '../../mol-data/int/sorted-array';
|
||||
import { Interval } from '../../mol-data/int/interval';
|
||||
import { OrderedSet } from '../../mol-data/int/ordered-set';
|
||||
|
||||
export const VolumeSegmentParams = {
|
||||
segments: PD.Converted(
|
||||
@@ -222,6 +224,7 @@ function getSegmentTexture(volume: Volume, segment: Volume.SegmentIndex, webgl:
|
||||
const gridDimension = Box3D.size(Vec3(), bbox);
|
||||
const { width, height, powerOfTwoSize: texDim } = getVolumeTexture2dLayout(gridDimension, Padding);
|
||||
const gridTexDim = Vec3.create(width, height, 0);
|
||||
const gridDataDim = Vec3.clone(gridDimension);
|
||||
const gridTexScale = Vec2.create(width / texDim, height / texDim);
|
||||
// console.log({ texDim, width, height, gridDimension });
|
||||
|
||||
@@ -247,6 +250,7 @@ function getSegmentTexture(volume: Volume, segment: Volume.SegmentIndex, webgl:
|
||||
transform,
|
||||
gridDimension,
|
||||
gridTexDim,
|
||||
gridDataDim,
|
||||
gridTexScale
|
||||
};
|
||||
}
|
||||
@@ -258,11 +262,11 @@ async function createVolumeSegmentTextureMesh(ctx: VisualContext, volume: Volume
|
||||
return TextureMesh.createEmpty(textureMesh);
|
||||
}
|
||||
|
||||
const { texture, gridDimension, gridTexDim, gridTexScale, transform } = getSegmentTexture(volume, segment, ctx.webgl);
|
||||
const { texture, gridDimension, gridTexDim, gridDataDim, gridTexScale, transform } = getSegmentTexture(volume, segment, ctx.webgl);
|
||||
|
||||
const axisOrder = volume.grid.cells.space.axisOrderSlowToFast as Vec3;
|
||||
const buffer = textureMesh?.doubleBuffer.get();
|
||||
const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, 0.5, false, false, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridDataDim, gridTexScale, transform, 0.5, false, false, axisOrder, true, buffer?.vertex, buffer?.group, buffer?.normal);
|
||||
|
||||
const groupCount = volume.grid.cells.data.length;
|
||||
const instances = Interval.ofLength(volume.instances.length as Volume.InstanceIndex);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user