ソースを参照

added structure volume repr, removed unit volume from molsurf repr

Alexander Rose 6 年 前
コミット
991151e962

+ 3 - 1
src/mol-data/int/ordered-set.ts

@@ -12,8 +12,10 @@ import SortedArray from './sorted-array';
 namespace OrderedSet {
     export const Empty: OrderedSet = Base.Empty as any;
     export const ofSingleton: <T extends number = number>(value: T) => OrderedSet<T> = Base.ofSingleton as any;
+    /** Create interval from range [min, max] */
     export const ofRange: <T extends number = number>(min: T, max: T) => OrderedSet<T> = Base.ofRange as any;
-    export const ofBounds: <T extends number = number>(min: T, max: T) => OrderedSet<T> = Base.ofBounds as any;
+    /** Create interval from bounds [start, end), i.e. [start, end - 1] */
+    export const ofBounds: <T extends number = number>(start: T, end: T) => OrderedSet<T> = Base.ofBounds as any;
     /** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
     export const ofSortedArray: <T extends number = number>(xs: ArrayLike<T>) => OrderedSet<T> = Base.ofSortedArray as any;
 

+ 6 - 3
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -72,8 +72,11 @@ export namespace DirectVolume {
     export const Params = {
         ...Geometry.Params,
         isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }),
-        renderMode: PD.Select('isosurface', RenderModeOptions),
-        controlPoints: PD.LineGraph([Vec2.create(0.19, 0.1), Vec2.create(0.2, 0.5), Vec2.create(0.21, 0.1), Vec2.create(0.4, 0.3)]),
+        renderMode: PD.Select('volume', RenderModeOptions),
+        controlPoints: PD.LineGraph([
+            Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.15), Vec2.create(0.21, 0.0),
+            Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.15), Vec2.create(0.81, 0.0),
+        ]),
         list: PD.ColorScale<ColorListName>('RedYellowBlue', ColorListOptions),
     }
     export type Params = typeof Params
@@ -140,7 +143,7 @@ export namespace DirectVolume {
 
     export function createRenderableState(props: PD.Values<Params>): RenderableState {
         const state = Geometry.createRenderableState(props)
-        updateRenderableState(state, props)
+        state.opaque = false
         return state
     }
 

+ 6 - 1
src/mol-math/geometry/gaussian-density.ts

@@ -16,10 +16,15 @@ const GaussianDensityGPU = typeof document !== 'undefined'
     ? (require('./gaussian-density/gpu') as typeof import('./gaussian-density/gpu')).GaussianDensityGPU
     : void 0;
 
-export const DefaultGaussianDensityProps = {
+export const DefaultGaussianDensityGPUProps = {
     resolution: 1,
     radiusOffset: 0,
     smoothness: 1.5,
+}
+export type GaussianDensityGPUProps = typeof DefaultGaussianDensityGPUProps
+
+export const DefaultGaussianDensityProps = {
+    ...DefaultGaussianDensityGPUProps,
     useGpu: true,
 }
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps

+ 6 - 6
src/mol-math/geometry/gaussian-density/gpu.ts

@@ -8,7 +8,7 @@
 import { RuntimeContext } from 'mol-task'
 import { PositionData, DensityData, DensityTextureData } from '../common'
 import { Box3D } from '../../geometry'
-import { GaussianDensityProps, getDelta } from '../gaussian-density'
+import { GaussianDensityGPUProps, getDelta } from '../gaussian-density'
 import { OrderedSet } from 'mol-data/int'
 import { Vec3, Tensor, Mat4 } from '../../linear-algebra'
 import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density'
@@ -23,7 +23,7 @@ import { decodeIdRGB } from 'mol-geo/geometry/picking';
 /** name for shared framebuffer used for gpu gaussian surface operations */
 const FramebufferName = 'gaussian-density-gpu'
 
-export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, webgl: WebGLContext): Promise<DensityData> {
+export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, webgl: WebGLContext): Promise<DensityData> {
     // always use texture2d when the gaussian density needs to be downloaded from the GPU,
     // it's faster than texture3d
     // console.time('GaussianDensityTexture2d')
@@ -38,7 +38,7 @@ export async function GaussianDensityGPU(ctx: RuntimeContext, position: Position
     return { field, idField, transform }
 }
 
-export async function GaussianDensityTexture(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, oldTexture?: Texture): Promise<DensityTextureData> {
+export async function GaussianDensityTexture(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): Promise<DensityTextureData> {
     // console.time(`GaussianDensityTexture, ${webgl.isWebGL2 ? '3d' : '2d'}`)
     const { texture, scale, bbox, dim } = webgl.isWebGL2 ?
         await GaussianDensityTexture3d(ctx, webgl, position, box, radius, props, oldTexture) :
@@ -54,7 +54,7 @@ export async function GaussianDensityTexture(ctx: RuntimeContext, webgl: WebGLCo
 
 //
 
-async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture) {
+async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture) {
     const { smoothness } = props
 
     const { drawCount, positions, radii, groups, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props)
@@ -115,7 +115,7 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext
     return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim }
 }
 
-async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture) {
+async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture) {
     const { smoothness } = props
 
     const { drawCount, positions, radii, groups, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props)
@@ -164,7 +164,7 @@ async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext
 
 //
 
-async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) {
+async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps) {
     const { resolution, radiusOffset } = props
 
     const { indices, x, y, z } = position

+ 28 - 2
src/mol-repr/structure/complex-visual.ts

@@ -7,8 +7,8 @@
 import { Structure } from 'mol-model/structure';
 import { Visual, VisualContext } from '../representation';
 import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
-import { createComplexMeshRenderObject, UnitKind, UnitKindOptions } from './visual/util/common';
-import { StructureMeshParams, StructureParams } from './representation';
+import { createComplexMeshRenderObject, UnitKind, UnitKindOptions, createComplexDirectVolumeRenderObject } from './visual/util/common';
+import { StructureMeshParams, StructureParams, StructureDirectVolumeParams } from './representation';
 import { deepEqual, ValueCell } from 'mol-util';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { Interval } from 'mol-data/int';
@@ -27,6 +27,7 @@ import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
 import { RenderableState } from 'mol-gl/renderable';
 import { UnitsParams } from './units-representation';
+import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 
 export interface  ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
 
@@ -227,4 +228,29 @@ export function ComplexMeshVisual<P extends ComplexMeshParams>(builder: ComplexM
         updateBoundingSphere: Mesh.updateBoundingSphere,
         updateRenderableState: Geometry.updateRenderableState
     })
+}
+
+// direct-volume
+
+export const ComplexDirectVolumeParams = {
+    ...StructureDirectVolumeParams,
+    unitKinds: PD.MultiSelect<UnitKind>(['atomic', 'spheres', 'gaussians'], UnitKindOptions),
+}
+export type ComplexDirectVolumeParams = typeof ComplexDirectVolumeParams
+
+export interface ComplexDirectVolumeVisualBuilder<P extends ComplexDirectVolumeParams> extends ComplexVisualBuilder<P, DirectVolume> { }
+
+export function ComplexDirectVolumeVisual<P extends ComplexDirectVolumeParams>(builder: ComplexDirectVolumeVisualBuilder<P>): ComplexVisual<P> {
+    return ComplexVisual<StructureDirectVolumeParams & UnitsParams>({
+        ...builder,
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
+        },
+        createEmptyGeometry: DirectVolume.createEmpty,
+        createRenderObject: createComplexDirectVolumeRenderObject,
+        updateValues: DirectVolume.updateValues,
+        updateBoundingSphere: DirectVolume.updateBoundingSphere,
+        updateRenderableState: DirectVolume.updateRenderableState
+    })
 }

+ 2 - 0
src/mol-repr/structure/registry.ts

@@ -9,6 +9,7 @@ import { RepresentationProvider, RepresentationRegistry } from '../representatio
 import { CartoonRepresentationProvider } from './representation/cartoon';
 import { BallAndStickRepresentationProvider } from './representation/ball-and-stick';
 import { MolecularSurfaceRepresentationProvider } from './representation/molecular-surface';
+import { MolecularVolumeRepresentationProvider } from './representation/molecular-volume';
 import { CarbohydrateRepresentationProvider } from './representation/carbohydrate';
 import { SpacefillRepresentationProvider } from './representation/spacefill';
 import { DistanceRestraintRepresentationProvider } from './representation/distance-restraint';
@@ -29,6 +30,7 @@ export const BuiltInStructureRepresentations = {
     'carbohydrate': CarbohydrateRepresentationProvider,
     'distance-restraint': DistanceRestraintRepresentationProvider,
     'molecular-surface': MolecularSurfaceRepresentationProvider,
+    'molecular-volume': MolecularVolumeRepresentationProvider,
     'spacefill': SpacefillRepresentationProvider,
 }
 export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations

+ 0 - 3
src/mol-repr/structure/representation/molecular-surface.ts

@@ -8,7 +8,6 @@ import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian
 import { UnitsRepresentation } from '../units-representation';
 import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume';
 import { StructureRepresentation, StructureRepresentationProvider } from '../representation';
 import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
@@ -17,7 +16,6 @@ import { Structure } from 'mol-model/structure';
 const MolecularSurfaceVisuals = {
     'gaussian-surface': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceParams>) => UnitsRepresentation('Gaussian surface', ctx, getParams, GaussianSurfaceVisual),
     'gaussian-wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianWireframeParams>) => UnitsRepresentation('Gaussian wireframe', ctx, getParams, GaussianWireframeVisual),
-    'gaussian-volume': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianDensityVolumeParams>) => UnitsRepresentation('Gaussian volume', ctx, getParams, GaussianDensityVolumeVisual)
 }
 type MolecularSurfaceVisualName = keyof typeof MolecularSurfaceVisuals
 const MolecularSurfaceVisualOptions = Object.keys(MolecularSurfaceVisuals).map(name => [name, name] as [MolecularSurfaceVisualName, string])
@@ -25,7 +23,6 @@ const MolecularSurfaceVisualOptions = Object.keys(MolecularSurfaceVisuals).map(n
 export const MolecularSurfaceParams = {
     ...GaussianSurfaceParams,
     ...GaussianWireframeParams,
-    ...GaussianDensityVolumeParams,
     visuals: PD.MultiSelect<MolecularSurfaceVisualName>(['gaussian-surface'], MolecularSurfaceVisualOptions),
 }
 export type MolecularSurfaceParams = typeof MolecularSurfaceParams

+ 39 - 0
src/mol-repr/structure/representation/molecular-volume.ts

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume';
+import { StructureRepresentation, StructureRepresentationProvider, ComplexRepresentation } from '../representation';
+import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
+import { ThemeRegistryContext } from 'mol-theme/theme';
+import { Structure } from 'mol-model/structure';
+
+const MolecularVolumeVisuals = {
+    'gaussian-volume': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianDensityVolumeParams>) => ComplexRepresentation('Gaussian volume', ctx, getParams, GaussianDensityVolumeVisual)
+}
+
+export const MolecularVolumeParams = {
+    ...GaussianDensityVolumeParams,
+}
+export type MolecularVolumeParams = typeof MolecularVolumeParams
+export function getMolecularVolumeParams(ctx: ThemeRegistryContext, structure: Structure) {
+    return PD.clone(MolecularVolumeParams)
+}
+
+export type MolecularVolumeRepresentation = StructureRepresentation<MolecularVolumeParams>
+export function MolecularVolumeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MolecularVolumeParams>): MolecularVolumeRepresentation {
+    return Representation.createMulti('Molecular Volume', ctx, getParams, MolecularVolumeVisuals as unknown as Representation.Def<Structure, MolecularVolumeParams>)
+}
+
+export const MolecularVolumeRepresentationProvider: StructureRepresentationProvider<MolecularVolumeParams> = {
+    label: 'Molecular Volume',
+    description: 'Displays a gaussian molecular density using direct volume rendering.',
+    factory: MolecularVolumeRepresentation,
+    getParams: getMolecularVolumeParams,
+    defaultValues: PD.getDefaultValues(MolecularVolumeParams),
+    defaultColorTheme: 'polymer-id',
+    defaultSizeTheme: 'uniform'
+}

+ 64 - 16
src/mol-repr/structure/visual/gaussian-density-volume.ts

@@ -4,42 +4,92 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../representation';
+import { Structure, StructureElement } from 'mol-model/structure';
 import { VisualUpdateState } from '../../util';
-import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visual';
-import { StructureElementIterator, getElementLoci, markElement } from './util/element';
-import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensityTexture } from 'mol-model/structure/structure/unit/gaussian-density';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
+import { ComplexVisual, ComplexDirectVolumeVisual, ComplexDirectVolumeParams } from '../complex-visual';
+import { EmptyLoci } from 'mol-model/loci';
+import { NullLocation } from 'mol-model/location';
+import { LocationIterator } from 'mol-geo/util/location-iterator';
+import { WebGLContext } from 'mol-gl/webgl/context';
+import { Texture } from 'mol-gl/webgl/texture';
+import { GaussianDensityTexture } from 'mol-math/geometry/gaussian-density/gpu';
+import { Task } from 'mol-task';
+import { OrderedSet } from 'mol-data/int';
+import { PhysicalSizeTheme } from 'mol-theme/size/physical';
 
-async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
+function getConformationAndRadius(structure: Structure) {
+    const n = structure.elementCount
+
+    const xs = new Float32Array(n)
+    const ys = new Float32Array(n)
+    const zs = new Float32Array(n)
+    const rs = new Float32Array(n)
+
+    const l = StructureElement.create()
+    const sizeTheme = PhysicalSizeTheme({}, {})
+
+    let m = 0
+    for (let i = 0, il = structure.units.length; i < il; ++i) {
+        const unit = structure.units[i]
+        const { elements } = unit
+        const { x, y, z } = unit.conformation
+        l.unit = unit
+        for (let j = 0, jl = elements.length; j < jl; ++j) {
+            const eI = elements[j]
+            xs[m + j] = x(eI)
+            ys[m + j] = y(eI)
+            zs[m + j] = z(eI)
+            l.element = eI
+            rs[m + j] = sizeTheme.size(l)
+        }
+        m += elements.length
+    }
+
+    const position = { indices: OrderedSet.ofRange(0, n), x: xs, y: ys, z: zs }
+    const radius = (index: number) => rs[index]
+
+    return { position, radius }
+}
+
+// TODO calculate by combining unit volumes
+function computeStructureGaussianDensityTexture(structure: Structure, props: PD.Values<GaussianDensityVolumeParams>, webgl: WebGLContext, texture?: Texture) {
+    const { position, radius } = getConformationAndRadius(structure)
+    return Task.create('Gaussian Density', async ctx => {
+        return await GaussianDensityTexture(ctx, webgl, position, structure.lookup3d.boundary.box, radius, props, texture);
+    });
+}
+
+async function createGaussianDensityVolume(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<GaussianDensityVolumeParams>, directVolume?: DirectVolume): Promise<DirectVolume> {
     const { runtime, webgl } = ctx
     if (webgl === undefined) throw new Error('createGaussianDensityVolume requires `webgl` object in VisualContext')
 
     const p = { ...props, useGpu: true }
     const oldTexture = directVolume ? directVolume.gridTexture.ref.value : undefined
-    const densityTextureData = await computeUnitGaussianDensityTexture(unit, p, webgl, oldTexture).runInContext(runtime)
+    const densityTextureData = await computeStructureGaussianDensityTexture(structure, p, webgl, oldTexture).runInContext(runtime)
     const { transform, texture, bbox, gridDimension } = densityTextureData
 
     return DirectVolume.create(bbox, gridDimension, transform, texture, directVolume)
 }
 
 export const GaussianDensityVolumeParams = {
-    ...UnitsDirectVolumeParams,
-    ...GaussianDensityParams,
+    ...ComplexDirectVolumeParams,
+    resolution: PD.Numeric(1, { min: 0.1, max: 10, step: 0.1 }),
+    radiusOffset: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }),
+    smoothness: PD.Numeric(1.5, { min: 0.5, max: 2.5, step: 0.1 }),
 }
 export type GaussianDensityVolumeParams = typeof GaussianDensityVolumeParams
 
-export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolumeParams> {
-    return UnitsDirectVolumeVisual<GaussianDensityVolumeParams>({
+export function GaussianDensityVolumeVisual(): ComplexVisual<GaussianDensityVolumeParams> {
+    return ComplexDirectVolumeVisual<GaussianDensityVolumeParams>({
         defaultProps: PD.getDefaultValues(GaussianDensityVolumeParams),
         createGeometry: createGaussianDensityVolume,
-        createLocationIterator: StructureElementIterator.fromGroup,
-        getLoci: getElementLoci,
-        mark: markElement,
+        createLocationIterator: (structure: Structure) => LocationIterator(structure.elementCount, 1, () => NullLocation),
+        getLoci: () => EmptyLoci, // TODO
+        mark: () => false, // TODO
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianDensityVolumeParams>, currentProps: PD.Values<GaussianDensityVolumeParams>) => {
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
@@ -47,8 +97,6 @@ export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolume
                 state.createGeometry = true
                 newProps.isoValue = Math.exp(-newProps.smoothness)
             }
-            if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
-            if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true
         }
     })
 }

+ 7 - 0
src/mol-repr/structure/visual/util/common.ts

@@ -103,6 +103,13 @@ export function createUnitsLinesRenderObject(group: Unit.SymmetryGroup, lines: L
 
 // direct-volume
 
+export function createComplexDirectVolumeRenderObject(structure: Structure, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureDirectVolumeParams>) {
+    const transform = createIdentityTransform()
+    const values = DirectVolume.createValues(directVolume, transform, locationIt, theme, props)
+    const state = DirectVolume.createRenderableState(props)
+    return createDirectVolumeRenderObject(values, state)
+}
+
 export function createUnitsDirectVolumeRenderObject(group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureDirectVolumeParams>) {
     const transform = createUnitsTransform(group)
     const values = DirectVolume.createValues(directVolume, transform, locationIt, theme, props)