Browse Source

wip, gaussian surface

Alexander Rose 6 years ago
parent
commit
84e6ebf57e

+ 3 - 0
src/apps/canvas/structure-view.ts

@@ -24,6 +24,7 @@ import { StructureRepresentation } from 'mol-geo/representation/structure';
 import { BehaviorSubject } from 'rxjs';
 import { SpacefillRepresentation } from 'mol-geo/representation/structure/representation/spacefill';
 import { DistanceRestraintRepresentation } from 'mol-geo/representation/structure/representation/distance-restraint';
+import { SurfaceRepresentation } from 'mol-geo/representation/structure/representation/surface';
 
 export interface StructureView {
     readonly viewer: Viewer
@@ -66,6 +67,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model>
     const active: { [k: string]: boolean } = {
         cartoon: true,
         point: false,
+        surface: true,
         ballAndStick: false,
         carbohydrate: false,
         spacefill: false,
@@ -76,6 +78,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model>
 
     const structureRepresentations: { [k: string]: StructureRepresentation<any> } = {
         cartoon: CartoonRepresentation(),
+        surface: SurfaceRepresentation(),
         point: PointRepresentation(),
         ballAndStick: BallAndStickRepresentation(),
         carbohydrate: CarbohydrateRepresentation(),

+ 47 - 0
src/mol-geo/representation/structure/representation/surface.ts

@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { UnitsRepresentation } from '..';
+import { GaussianSurfaceVisual, DefaultGaussianSurfaceProps } from '../visual/gaussian-surface-mesh';
+import { StructureRepresentation } from '../units-representation';
+import { Structure } from 'mol-model/structure';
+import { MarkerAction } from '../../../util/marker-data';
+import { Loci } from 'mol-model/loci';
+import { PickingId } from '../../../util/picking';
+
+export const DefaultSurfaceProps = {
+    ...DefaultGaussianSurfaceProps,
+}
+export type SurfaceProps = typeof DefaultSurfaceProps
+
+export type SurfaceRepresentation = StructureRepresentation<SurfaceProps>
+
+export function SurfaceRepresentation(): SurfaceRepresentation {
+    let currentProps: SurfaceProps
+    const gaussianRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual)
+    return {
+        label: 'Surface',
+        get renderObjects() {
+            return [ ...gaussianRepr.renderObjects ]
+        },
+        get props() {
+            return { ...gaussianRepr.props }
+        },
+        createOrUpdate: (props: Partial<SurfaceProps> = {}, structure?: Structure) => {
+            currentProps = Object.assign({}, DefaultSurfaceProps, currentProps, props)
+            return gaussianRepr.createOrUpdate(currentProps, structure)
+        },
+        getLoci: (pickingId: PickingId) => {
+            return gaussianRepr.getLoci(pickingId)
+        },
+        mark: (loci: Loci, action: MarkerAction) => {
+            return gaussianRepr.mark(loci, action)
+        },
+        destroy() {
+            gaussianRepr.destroy()
+        }
+    }
+}

+ 3 - 3
src/mol-geo/representation/structure/units-visual.ts

@@ -32,7 +32,7 @@ export type UnitsMeshProps = typeof DefaultUnitsMeshProps
 
 export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> {
     defaultProps: P
-    createMesh(ctx: RuntimeContext, unit: Unit, props: P, mesh?: Mesh): Promise<Mesh>
+    createMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, mesh?: Mesh): Promise<Mesh>
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
@@ -59,7 +59,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
         const unit = group.units[0]
         currentConformationId = Unit.conformationId(unit)
         mesh = currentProps.unitKinds.includes(unit.kind)
-            ? await createMesh(ctx, unit, currentProps, mesh)
+            ? await createMesh(ctx, unit, currentStructure, currentProps, mesh)
             : Mesh.createEmpty(mesh)
 
         // TODO create empty location iterator when not in unitKinds
@@ -102,7 +102,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
 
         if (updateState.createMesh) {
             mesh = newProps.unitKinds.includes(unit.kind)
-                ? await createMesh(ctx, unit, newProps, mesh)
+                ? await createMesh(ctx, unit, currentStructure, newProps, mesh)
                 : Mesh.createEmpty(mesh)
             ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
             updateState.updateColor = true

+ 153 - 0
src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts

@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Unit, Structure } from 'mol-model/structure';
+import { UnitsVisual, MeshUpdateState } from '..';
+import { RuntimeContext } from 'mol-task'
+import { Mesh } from '../../../mesh/mesh';
+import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
+import { StructureElementIterator, getElementLoci, markElement } from './util/element';
+import { computeMarchingCubes } from '../../../util/marching-cubes/algorithm';
+import { Tensor, Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { Box3D } from 'mol-math/geometry';
+import { ValueCell } from 'mol-util';
+import { smoothstep } from 'mol-math/interpolate';
+
+export interface GaussianSurfaceMeshProps {
+    
+}
+
+function getDelta(box: Box3D) {
+    const extent = Vec3.sub(Vec3.zero(), box.max, box.min)
+
+    const n = Math.pow(128, 3)
+    const f = (extent[0] * extent[1] * extent[2]) / n
+    const s = Math.pow(f, 1 / 3)
+    const size = Vec3.zero()
+    // Vec3.scale(size, extent, s)
+    Vec3.ceil(size, Vec3.scale(size, extent, s))
+    const delta = Vec3.div(Vec3.zero(), extent, size)
+    return delta
+}
+
+async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianSurfaceMeshProps, mesh?: Mesh): Promise<Mesh> {
+
+    const { elements } = unit;
+    const elementCount = elements.length;
+
+    const r = 2.5;
+
+    const v = Vec3.zero()
+    const p = Vec3.zero()
+    const pos = unit.conformation.invariantPosition
+    const box = unit.lookup3d.boundary.box
+    const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(r*3, r*3, r*3));
+    const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
+    const min = expandedBox.min
+
+    // const n = Math.pow(128, 3)
+    // const f = (extent[0] * extent[1] * extent[2]) / n
+    // const s = Math.pow(f, 1 / 3)
+    // const size = Vec3.zero()
+    // // Vec3.scale(size, extent, s)
+    // Vec3.ceil(size, Vec3.scale(size, extent, s))
+    // const delta = Vec3.div(Vec3.zero(), extent, size)
+
+    // console.log('extent', extent)
+    // console.log('n', n)
+    // console.log('f', f)
+    // console.log('s', s)
+    // console.log('size', size)
+    // console.log('delta', delta)
+    const delta = getDelta(Box3D.expand(Box3D.empty(), structure.boundary.box, Vec3.create(r*3, r*3, r*3)))
+    const dim = Vec3.zero()
+    Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
+    // console.log('dim', dim, dim[0] * dim[1] * dim[2])
+
+    const space = Tensor.Space(dim, [0, 1, 2], Float32Array)
+    const data = space.create()
+    const field = Tensor.create(space, data)
+
+    for (let i = 0; i < elementCount; i++) {
+        pos(elements[i], v)
+
+        Vec3.mul(v, Vec3.sub(v, v, min), delta)
+
+        const size = r
+        const radius = size * delta[0]
+
+        const minX = Math.floor(v[0] - radius)
+        const minY = Math.floor(v[1] - radius)
+        const minZ = Math.floor(v[2] - radius)
+        const maxX = Math.floor(v[0] + radius)
+        const maxY = Math.floor(v[1] + radius)
+        const maxZ = Math.floor(v[2] + radius)
+
+        for (let x = minX; x <= maxX; ++x) {
+            for (let y = minY; y <= maxY; ++y) {
+                for (let z = minZ; z <= maxZ; ++z) {
+                    const dist = Vec3.distance(Vec3.set(p, x, y, z), v)
+                    if (dist <= radius) {
+                        const density = 1.0 - smoothstep(0.0, radius * 1.0, dist)
+                        space.set(data, x, y, z, space.get(data, x, y, z) + density)
+                    }
+                } 
+            }   
+        }
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Gaussian surface', current: i, max: elementCount });
+        }
+    }
+
+    // console.log('data', data)
+
+    const surface = await computeMarchingCubes({
+        isoLevel: 0.1,
+        scalarField: field,
+        oldSurface: mesh
+        
+    }).runAsChild(ctx);
+
+    const t = Mat4.identity()
+    Mat4.fromUniformScaling(t, 1 / delta[0])
+    Mat4.setTranslation(t, expandedBox.min)
+
+    ValueCell.update(surface.groupBuffer, new Float32Array(surface.vertexCount)) 
+    Mesh.transformImmediate(surface, t)
+    await Mesh.computeNormals(surface).runAsChild(ctx)
+
+    // console.log('surface', surface)
+
+    // const transform = VolumeData.getGridToCartesianTransform(volume);
+    // ctx.update({ message: 'Transforming mesh...' });
+    // Mesh.transformImmediate(surface, transform);
+
+    return surface;
+}
+
+export const DefaultGaussianSurfaceProps = {
+    ...DefaultUnitsMeshProps,
+    linearSegments: 8,
+    radialSegments: 12,
+    aspectRatio: 5,
+    arrowFactor: 1.5,
+
+    flipSided: true,
+    // flatShaded: true,
+}
+export type GaussianSurfaceProps = typeof DefaultGaussianSurfaceProps
+
+export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> {
+    return UnitsMeshVisual<GaussianSurfaceProps>({
+        defaultProps: DefaultGaussianSurfaceProps,
+        createMesh: createGaussianSurfaceMesh,
+        createLocationIterator: StructureElementIterator.fromGroup,
+        getLoci: getElementLoci,
+        mark: markElement,
+        setUpdateState: (state: MeshUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => {}
+    })
+}

+ 2 - 2
src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts

@@ -5,7 +5,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Unit, Link, StructureElement } from 'mol-model/structure';
+import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
 import { UnitsVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link';
@@ -18,7 +18,7 @@ import { Interval } from 'mol-data/int';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { BitFlags } from 'mol-util';
 
-async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, props: LinkCylinderProps, mesh?: Mesh) {
+async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     const sizeTheme = SizeTheme(props.sizeTheme)

+ 2 - 2
src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from 'mol-model/structure';
+import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../mesh/mesh';
@@ -33,7 +33,7 @@ const sVec = Vec3.zero()
 const box = Box()
 
 // TODO define props, should be scalable
-async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props: {}, mesh?: Mesh) {
+async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: {}, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     // TODO better vertex count estimate

+ 2 - 2
src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from 'mol-model/structure';
+import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../mesh/mesh';
@@ -23,7 +23,7 @@ export interface PolymerBackboneCylinderProps {
     radialSegments: number
 }
 
-async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
+async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 

+ 2 - 2
src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from 'mol-model/structure';
+import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../mesh/mesh';
@@ -32,7 +32,7 @@ export interface PolymerDirectionWedgeProps {
     sizeTheme: SizeThemeProps
 }
 
-async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
+async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 

+ 2 - 2
src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from 'mol-model/structure';
+import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../mesh/mesh';
@@ -24,7 +24,7 @@ export interface PolymerGapCylinderProps {
     radialSegments: number
 }
 
-async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, props: PolymerGapCylinderProps, mesh?: Mesh) {
+async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerGapCylinderProps, mesh?: Mesh) {
     const polymerGapCount = unit.gapElements.length
     if (!polymerGapCount) return Mesh.createEmpty(mesh)
 

+ 2 - 2
src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from 'mol-model/structure';
+import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../mesh/mesh';
@@ -26,7 +26,7 @@ export interface PolymerTraceMeshProps {
 
 // TODO handle polymer ends properly
 
-async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, props: PolymerTraceMeshProps, mesh?: Mesh) {
+async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerTraceMeshProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 

+ 2 - 2
src/mol-geo/representation/structure/visual/util/element.ts

@@ -5,7 +5,7 @@
  */
 
 import { Vec3 } from 'mol-math/linear-algebra';
-import { Unit, StructureElement } from 'mol-model/structure';
+import { Unit, StructureElement, Structure } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task';
 import { sphereVertexCount } from '../../../../primitive/sphere';
 import { Mesh } from '../../../../mesh/mesh';
@@ -22,7 +22,7 @@ export interface ElementSphereMeshProps {
     detail: number,
 }
 
-export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, props: ElementSphereMeshProps, mesh?: Mesh) {
+export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementSphereMeshProps, mesh?: Mesh) {
     const { detail } = props
 
     const { elements } = unit;