Skip to content
Snippets Groups Projects
Commit ab6a0935 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, mol surf

parent e0c6ebd2
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Fred Ludlow <fred.ludlow@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* ported from NGL (https://github.com/arose/ngl), licensed under MIT
*/
import { fillUniform } from 'mol-util/array';
import { Vec3 } from 'mol-math/linear-algebra';
import { NumberArray } from 'mol-util/type-helpers';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { number } from 'prop-types';
import { Lookup3D, Result } from './lookup3d/common';
import { RuntimeContext } from 'mol-task';
import { OrderedSet } from 'mol-data/int';
import { PositionData } from './common';
function normalToLine (out: Vec3, p: Vec3) {
out[0] = out[1] = out[2] = 1.0
if (p[0] !== 0) {
out[0] = (p[1] + p[2]) / -p[0]
} else if (p[1] !== 0) {
out[1] = (p[0] + p[2]) / -p[1]
} else if (p[2] !== 0) {
out[2] = (p[0] + p[1]) / -p[2]
}
return out
}
function fillGridDim (a: Float32Array, start: number, step: number) {
for (let i = 0; i < a.length; i++) {
a[i] = start + (step * i)
}
}
type AnglesTables = { cosTable: Float32Array, sinTable: Float32Array }
function getAngleTables (probePositions: number): AnglesTables {
let theta = 0.0
const step = 2 * Math.PI / probePositions
const cosTable = new Float32Array(probePositions)
const sinTable = new Float32Array(probePositions)
for (let i = 0; i < probePositions; i++) {
cosTable[ i ] = Math.cos(theta)
sinTable[ i ] = Math.sin(theta)
theta += step
}
return { cosTable, sinTable}
}
/**
* Is the point at x,y,z obscured by any of the atoms specifeid by indices in neighbours.
* Ignore indices a and b (these are the relevant atoms in projectPoints/Torii)
*
* Cache the last clipped atom (as very often the same one in subsequent calls)
*/
function obscured (state: MolSurfCalcState, x: number, y: number, z: number, a: number, b: number) {
let ai: number
if (state.lastClip !== -1) {
ai = state.lastClip
if (ai !== a && ai !== b && singleAtomObscures(state, ai, x, y, z)) {
return ai
} else {
state.lastClip = -1
}
}
let ni = 0
ai = state.neighbours[ni]
while (ai >= 0) {
if (ai !== a && ai !== b && singleAtomObscures(state, ai, x, y, z)) {
state.lastClip = ai
return ai
}
ai = state.neighbours[++ni]
}
state.lastClip = -1
return -1
}
function singleAtomObscures (state: MolSurfCalcState, ai: number, x: number, y: number, z: number) {
const ra2 = state.radiusSq[ai]
const dx = state.xCoord[ai] - x
const dy = state.yCoord[ai] - y
const dz = state.zCoord[ai] - z
const d2 = dx * dx + dy * dy + dz * dz
return d2 < ra2
}
/**
* For each atom:
* Iterate over a subsection of the grid, for each point:
* If current value < 0.0, unvisited, set positive
*
* In any case: Project this point onto surface of the atomic sphere
* If this projected point is not obscured by any other atom
* Calculate delta distance and set grid value to minimum of
* itself and delta
*/
async function projectPoints (ctx: RuntimeContext, state: MolSurfCalcState) {
const { position, radius, scaleFactor, lookup3D, min, delta } = state
const { indices, x, y, z } = position
const n = OrderedSet.size(indices)
const v = Vec3()
const p = Vec3()
const c = Vec3()
const beg = Vec3()
const end = Vec3()
const gridPad = 1 / Math.max(...delta)
for (let i = 0; i < n; ++i) {
const j = OrderedSet.getAt(indices, i)
Vec3.set(v, x[j], y[j], z[j])
Vec3.sub(v, v, min)
Vec3.mul(c, v, delta)
const rad = radius(j)
const rSq = rad * rad
const r2 = rad * 2 + gridPad
const rad2 = Vec3.create(r2, r2, r2)
Vec3.mul(rad2, rad2, delta)
const r2sq = r2 * r2
const [ begX, begY, begZ ] = Vec3.floor(beg, Vec3.sub(beg, c, rad2))
const [ endX, endY, endZ ] = Vec3.ceil(end, Vec3.add(end, c, rad2))
for (let xi = begX; xi < endX; ++xi) {
for (let yi = begY; yi < endY; ++yi) {
for (let zi = begZ; zi < endZ; ++zi) {
Vec3.set(p, xi, yi, zi)
Vec3.div(p, p, delta)
const distSq = Vec3.squaredDistance(p, v)
if (distSq <= r2sq) {
const dens = Math.exp(-alpha * (distSq / rSq))
space.add(data, xi, yi, zi, dens)
if (dens > space.get(densData, xi, yi, zi)) {
space.set(densData, xi, yi, zi, dens)
space.set(idData, xi, yi, zi, i)
}
}
}
}
}
if (i % 10000 === 0 && ctx.shouldUpdate) {
await ctx.update({ message: 'projecting points', current: i, max: n })
}
}
for (let i = 0; i < nAtoms; i++) {
const ax = xCoord[ i ]
const ay = yCoord[ i ]
const az = zCoord[ i ]
const ar = radius[ i ]
const ar2 = radiusSq[ i ]
state.neighbours = lookup3D.find(ax, ay, az, ar)
// Number of grid points, round this up...
const ng = Math.ceil(ar * scaleFactor)
// Center of the atom, mapped to grid points (take floor)
const iax = Math.floor(scaleFactor * (ax - min[ 0 ]))
const iay = Math.floor(scaleFactor * (ay - min[ 1 ]))
const iaz = Math.floor(scaleFactor * (az - min[ 2 ]))
// Extents of grid to consider for this atom
const minx = Math.max(0, iax - ng)
const miny = Math.max(0, iay - ng)
const minz = Math.max(0, iaz - ng)
// Add two to these points:
// - iax are floor'd values so this ensures coverage
// - these are loop limits (exclusive)
const maxx = Math.min(dim[ 0 ], iax + ng + 2)
const maxy = Math.min(dim[ 1 ], iay + ng + 2)
const maxz = Math.min(dim[ 2 ], iaz + ng + 2)
for (let ix = minx; ix < maxx; ix++) {
const dx = gridx[ ix ] - ax
const xoffset = dim[ 1 ] * dim[ 2 ] * ix
for (let iy = miny; iy < maxy; iy++) {
const dy = gridy[ iy ] - ay
const dxy2 = dx * dx + dy * dy
const xyoffset = xoffset + dim[ 2 ] * iy
for (let iz = minz; iz < maxz; iz++) {
const dz = gridz[ iz ] - az
const d2 = dxy2 + dz * dz
if (d2 < ar2) {
const idx = iz + xyoffset
if (grid[idx] < 0.0) {
// Unvisited, make positive
grid[ idx ] = -grid[ idx ]
}
// Project on to the surface of the sphere
// sp is the projected point ( dx, dy, dz ) * ( ra / d )
const d = Math.sqrt(d2)
const ap = ar / d
let spx = dx * ap
let spy = dy * ap
let spz = dz * ap
spx += ax
spy += ay
spz += az
if (obscured(state, spx, spy, spz, i, -1) === -1) {
const dd = ar - d
if (dd < grid[ idx ]) {
grid[ idx ] = dd
atomIndex[ idx ] = i
}
}
}
}
}
}
}
}
// Vectors for Torus Projection
const atob = Vec3()
const mid = Vec3()
const n1 = Vec3()
const n2 = Vec3()
function projectTorus (state: MolSurfCalcState, a: number, b: number) {
const r1 = state.radius[a]
const r2 = state.radius[b]
const dx = atob[0] = state.xCoord[b] - state.xCoord[a]
const dy = atob[1] = state.yCoord[b] - state.yCoord[a]
const dz = atob[2] = state.zCoord[b] - state.zCoord[a]
const d2 = dx * dx + dy * dy + dz * dz
// This check now redundant as already done in AVHash.withinRadii
// if (d2 > ((r1 + r2) * (r1 + r2))){ return; }
const d = Math.sqrt(d2)
// Find angle between a->b vector and the circle
// of their intersection by cosine rule
const cosA = (r1 * r1 + d * d - r2 * r2) / (2.0 * r1 * d)
// distance along a->b at intersection
const dmp = r1 * cosA
Vec3.normalize(atob, atob)
// Create normal to line
normalToLine(n1, atob)
Vec3.normalize(n1, n1)
// Cross together for second normal vector
Vec3.cross(n2, atob, n1)
Vec3.normalize(n2, n2)
// r is radius of circle of intersection
const rInt = Math.sqrt(r1 * r1 - dmp * dmp)
Vec3.scale(n1, n1, rInt)
Vec3.scale(n2, n2, rInt)
Vec3.scale(atob, atob, dmp)
mid[0] = atob[0] + state.xCoord[a]
mid[1] = atob[1] + state.yCoord[a]
mid[2] = atob[2] + state.zCoord[a]
state.lastClip = -1
const { ngTorus, cosTable, sinTable, scaleFactor } = state
for (let i = 0; i < state.probePositions; i++) {
const cost = cosTable[ i ]
const sint = sinTable[ i ]
const px = mid[0] + cost * n1[0] + sint * n2[0]
const py = mid[1] + cost * n1[1] + sint * n2[1]
const pz = mid[2] + cost * n1[2] + sint * n2[2]
if (obscured(state, px, py, pz, a, b) === -1) {
// As above, iterate over our grid...
// px, py, pz in grid coords
const iax = Math.floor(scaleFactor * (px - min[0]))
const iay = Math.floor(scaleFactor * (py - min[1]))
const iaz = Math.floor(scaleFactor * (pz - min[2]))
const minx = Math.max(0, iax - ngTorus)
const miny = Math.max(0, iay - ngTorus)
const minz = Math.max(0, iaz - ngTorus)
const maxx = Math.min(dim[0], iax + ngTorus + 2)
const maxy = Math.min(dim[1], iay + ngTorus + 2)
const maxz = Math.min(dim[2], iaz + ngTorus + 2)
for (let ix = minx; ix < maxx; ix++) {
const dx = px - gridx[ ix ]
const xoffset = dim[1] * dim[2] * ix
for (let iy = miny; iy < maxy; iy++) {
const dy = py - gridy[iy]
const dxy2 = dx * dx + dy * dy
const xyoffset = xoffset + dim[2] * iy
for (let iz = minz; iz < maxz; iz++) {
const dz = pz - gridz[iz]
const d2 = dxy2 + dz * dz
const idx = iz + xyoffset
const current = grid[idx]
if (current > 0.0 && d2 < (current * current)) {
grid[idx] = Math.sqrt(d2)
// Is this grid point closer to a or b?
// Take dot product of atob and gridpoint->p (dx, dy, dz)
const dp = dx * atob[0] + dy * atob [1] + dz * atob[2]
atomIndex[idx] = dp < 0.0 ? b : a
}
}
}
}
}
}
}
function projectTorii (state: MolSurfCalcState) {
const { n: nAtoms, neighbours, hash, xCoord, yCoord, zCoord, radius } = state
for (let i = 0; i < nAtoms; i++) {
hash.withinRadii(xCoord[i], yCoord[i], zCoord[i], radius[i], neighbours)
let ia = 0
let ni = neighbours[ ia ]
while (ni >= 0) {
if (i < ni) {
projectTorus(state, i, ni)
}
ni = neighbours[ ++ia ]
}
}
}
function fixNegatives (grid: NumberArray) {
for (let i = 0; i < grid.length; i++) {
if (grid[i] < 0) grid[i] = 0
}
}
function fixAtomIDs (atomIndex: NumberArray, indexList: NumberArray) {
for (let i = 0; i < atomIndex.length; i++) {
atomIndex[i] = indexList[atomIndex[i]]
}
}
//
interface MolSurfCalcState {
/** Cached last value for obscured test */
lastClip: number
/** Neighbours as transient result array from lookup3d */
neighbours: Result<number>
lookup3D: Lookup3D
position: PositionData
radius: (index: number) => number
delta: Vec3
min: Vec3
maxRadius: number
n: number
scaleFactor: number
/** Angle lookup tables */
cosTable: Float32Array
sinTable: Float32Array
probePositions: number
ngTorus: number
}
export const MolecularSurfaceCalculationParams = {
scaleFactor: PD.Numeric(2, { min: 0.1, max: 10, step: 0.1 }),
probeRadius: PD.Numeric(1.4, { min: 0, max: 10, step: 0.1 }),
probePositions: PD.Numeric(30, { min: 12, max: 90, step: 1 }),
}
export const DefaultMolecularSurfaceCalculationProps = PD.getDefaultValues(MolecularSurfaceCalculationParams)
export type MolecularSurfaceCalculationProps = typeof DefaultMolecularSurfaceCalculationProps
function createState(nAtoms: number, props: MolecularSurfaceCalculationProps): MolSurfCalcState {
const { scaleFactor, probeRadius, probePositions } = props
const { cosTable, sinTable } = getAngleTables(probePositions)
const ngTorus = Math.max(5, 2 + Math.floor(probeRadius * scaleFactor))
return {
lastClip: -1,
neighbours: Int32Array,
xCoord: new Float32Array(nAtoms),
yCoord: new Float32Array(nAtoms),
zCoord: new Float32Array(nAtoms),
radius: new Float32Array(nAtoms),
radiusSq: new Float32Array(nAtoms),
maxRadius: 0,
n: nAtoms,
scaleFactor,
cosTable,
sinTable,
probePositions,
ngTorus,
}
}
//
export function MolecularSurface(coordList: Float32Array, radiusList: Float32Array, indexList: Uint16Array|Uint32Array) {
// Field generation method adapted from AstexViewer (Mike Hartshorn)
// by Fred Ludlow.
// Other parts based heavily on NGL (Alexander Rose) EDT Surface class
//
// Should work as a drop-in alternative to EDTSurface (though some of
// the EDT paramters are not relevant in this method).
const nAtoms = radiusList.length
const x = new Float32Array(nAtoms)
const y = new Float32Array(nAtoms)
const z = new Float32Array(nAtoms)
for (let i = 0; i < nAtoms; i++) {
const ci = 3 * i
x[ i ] = coordList[ ci ]
y[ i ] = coordList[ ci + 1 ]
z[ i ] = coordList[ ci + 2 ]
}
let bbox = computeBoundingBox(coordList)
if (coordList.length === 0) {
bbox[ 0 ].set([ 0, 0, 0 ])
bbox[ 1 ].set([ 0, 0, 0 ])
}
const min = bbox[0]
const max = bbox[1]
let r: Float32Array, r2: Float32Array // Atom positions, expanded radii (squared)
let maxRadius: number
// Parameters
let probeRadius: number, scaleFactor: number, setAtomID: boolean, probePositions: number
// Grid params
let dim: Float32Array, matrix: Float32Array, grid: NumberArray, atomIndex: Int32Array
// grid indices -> xyz coords
let gridx: Float32Array, gridy: Float32Array, gridz: Float32Array
// Spatial Hash
let hash: iAVHash
// Neighbour array to be filled by hash
let neighbours: Int32Array
let ngTorus: number
function init (_probeRadius?: number, _scaleFactor?: number, _setAtomID?: boolean, _probePositions?: number) {
probeRadius = defaults(_probeRadius, 1.4)
scaleFactor = defaults(_scaleFactor, 2.0)
setAtomID = defaults(_setAtomID, true)
probePositions = defaults(_probePositions, 30)
r = new Float32Array(nAtoms)
r2 = new Float32Array(nAtoms)
for (let i = 0; i < r.length; ++i) {
var rExt = radiusList[ i ] + probeRadius
r[ i ] = rExt
r2[ i ] = rExt * rExt
}
maxRadius = 0
for (let j = 0; j < r.length; ++j) {
if (r[ j ] > maxRadius) maxRadius = r[ j ]
}
initializeGrid()
getAngleTables(probePositions)
initializeHash()
lastClip = -1
}
function initializeGrid () {
const surfGrid = getSurfaceGrid(
min, max, maxRadius, scaleFactor, 0.0
)
scaleFactor = surfGrid.scaleFactor
dim = surfGrid.dim
matrix = surfGrid.matrix
ngTorus = Math.max(5, 2 + Math.floor(probeRadius * scaleFactor))
grid = fillUniform(new Float32Array(dim[0] * dim[1] * dim[2]), -1001.0)
atomIndex = new Int32Array(grid.length)
gridx = new Float32Array(dim[0])
gridy = new Float32Array(dim[1])
gridz = new Float32Array(dim[2])
fillGridDim(gridx, min[0], 1 / scaleFactor)
fillGridDim(gridy, min[1], 1 / scaleFactor)
fillGridDim(gridz, min[2], 1 / scaleFactor)
}
function initializeHash () {
hash = makeAVHash(x, y, z, r, min, max, 2.01 * maxRadius)
neighbours = new Int32Array(hash.neighbourListLength)
}
function getVolume (probeRadius: number, scaleFactor: number, setAtomID: boolean) {
// Basic steps are:
// 1) Initialize
// 2) Project points
// 3) Project torii
console.time('AVSurface.getVolume')
console.time('AVSurface.init')
init(probeRadius, scaleFactor, setAtomID)
console.timeEnd('AVSurface.init')
console.time('AVSurface.projectPoints')
projectPoints()
console.timeEnd('AVSurface.projectPoints')
console.time('AVSurface.projectTorii')
projectTorii()
console.timeEnd('AVSurface.projectTorii')
fixNegatives()
fixAtomIDs()
console.timeEnd('AVSurface.getVolume')
}
}
\ No newline at end of file
...@@ -16,6 +16,7 @@ import { DistanceRestraintRepresentationProvider } from './representation/distan ...@@ -16,6 +16,7 @@ import { DistanceRestraintRepresentationProvider } from './representation/distan
import { PointRepresentationProvider } from './representation/point'; import { PointRepresentationProvider } from './representation/point';
import { StructureRepresentationState } from './representation'; import { StructureRepresentationState } from './representation';
import { PuttyRepresentationProvider } from './representation/putty'; import { PuttyRepresentationProvider } from './representation/putty';
import { MolecularSurfaceRepresentationProvider } from './representation/molecular-surface';
export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> { export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
constructor() { constructor() {
...@@ -34,6 +35,7 @@ export const BuiltInStructureRepresentations = { ...@@ -34,6 +35,7 @@ export const BuiltInStructureRepresentations = {
'distance-restraint': DistanceRestraintRepresentationProvider, 'distance-restraint': DistanceRestraintRepresentationProvider,
'gaussian-surface': GaussianSurfaceRepresentationProvider, 'gaussian-surface': GaussianSurfaceRepresentationProvider,
'gaussian-volume': GaussianVolumeRepresentationProvider, 'gaussian-volume': GaussianVolumeRepresentationProvider,
'molecular-surface': MolecularSurfaceRepresentationProvider,
'point': PointRepresentationProvider, 'point': PointRepresentationProvider,
'putty': PuttyRepresentationProvider, 'putty': PuttyRepresentationProvider,
'spacefill': SpacefillRepresentationProvider, 'spacefill': SpacefillRepresentationProvider,
......
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { MolecularSurfaceMeshVisual, MolecularSurfaceMeshParams } from '../visual/molecular-surface-mesh';
import { UnitsRepresentation } from '../units-representation';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder } from '../representation';
import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
import { ThemeRegistryContext } from 'mol-theme/theme';
import { Structure } from 'mol-model/structure';
const MolecularSurfaceVisuals = {
'molecular-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MolecularSurfaceMeshParams>) => UnitsRepresentation('Molecular surface', ctx, getParams, MolecularSurfaceMeshVisual),
}
type MolecularSurfaceVisualName = keyof typeof MolecularSurfaceVisuals
const MolecularSurfaceVisualOptions = Object.keys(MolecularSurfaceVisuals).map(name => [name, name] as [MolecularSurfaceVisualName, string])
export const MolecularSurfaceParams = {
...MolecularSurfaceMeshParams,
visuals: PD.MultiSelect<MolecularSurfaceVisualName>(['molecular-surface-mesh'], MolecularSurfaceVisualOptions),
}
export type MolecularSurfaceParams = typeof MolecularSurfaceParams
export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {
return PD.clone(MolecularSurfaceParams)
}
export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceParams>
export function MolecularSurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MolecularSurfaceParams>): MolecularSurfaceRepresentation {
return Representation.createMulti('Molecular Surface', ctx, getParams, StructureRepresentationStateBuilder, MolecularSurfaceVisuals as unknown as Representation.Def<Structure, MolecularSurfaceParams>)
}
export const MolecularSurfaceRepresentationProvider: StructureRepresentationProvider<MolecularSurfaceParams> = {
label: 'Molecular Surface',
description: 'Displays a molecular surface.',
factory: MolecularSurfaceRepresentation,
getParams: getMolecularSurfaceParams,
defaultValues: PD.getDefaultValues(MolecularSurfaceParams),
defaultColorTheme: 'polymer-id',
defaultSizeTheme: 'uniform'
}
\ No newline at end of file
/**
* Copyright (c) 2019 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 } from '../representation';
import { VisualUpdateState } from '../../util';
import { UnitsMeshVisual, UnitsMeshParams, UnitsTextureMeshParams } from '../units-visual';
import { StructureElementIterator, getElementLoci, eachElement } from './util/element';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { Mesh } from 'mol-geo/geometry/mesh/mesh';
import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
import { VisualContext } from 'mol-repr/visual';
import { Theme } from 'mol-theme/theme';
import { MolecularSurfaceCalculationParams, MolecularSurfaceCalculationProps, computeUnitMolecularSurface } from './util/molecular-surface';
export const MolecularSurfaceMeshParams = {
...UnitsMeshParams,
...UnitsTextureMeshParams,
...MolecularSurfaceCalculationParams,
}
export type MolecularSurfaceMeshParams = typeof MolecularSurfaceMeshParams
//
async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: MolecularSurfaceCalculationProps, mesh?: Mesh): Promise<Mesh> {
const { transform, field, idField } = await computeUnitMolecularSurface(unit, props).runInContext(ctx.runtime)
const params = {
isoLevel: 1,
scalarField: field,
idField
}
const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx.runtime)
Mesh.transformImmediate(surface, transform)
Mesh.computeNormalsImmediate(surface)
Mesh.uniformTriangleGroup(surface)
return surface
}
export function MolecularSurfaceMeshVisual(materialId: number): UnitsVisual<MolecularSurfaceMeshParams> {
return UnitsMeshVisual<MolecularSurfaceMeshParams>({
defaultProps: PD.getDefaultValues(MolecularSurfaceMeshParams),
createGeometry: createMolecularSurfaceMesh,
createLocationIterator: StructureElementIterator.fromGroup,
getLoci: getElementLoci,
eachLocation: eachElement,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<MolecularSurfaceMeshParams>, currentProps: PD.Values<MolecularSurfaceMeshParams>) => {
if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
if (newProps.probeRadius !== currentProps.probeRadius) state.createGeometry = true
}
}, materialId)
}
\ No newline at end of file
/**
* Copyright (c) 2019 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 { Unit } from 'mol-model/structure';
import { Task, RuntimeContext } from 'mol-task';
import { getUnitConformationAndRadius } from './common';
import { PositionData, Box3D, DensityData } from 'mol-math/geometry';
export const MolecularSurfaceCalculationParams = {
resolution: PD.Numeric(1, { min: 0.1, max: 10, step: 0.1 }),
probeRadius: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }),
}
export const DefaultMolecularSurfaceCalculationProps = PD.getDefaultValues(MolecularSurfaceCalculationParams)
export type MolecularSurfaceCalculationProps = typeof DefaultMolecularSurfaceCalculationProps
export function computeUnitMolecularSurface(unit: Unit, props: MolecularSurfaceCalculationProps) {
const { position, radius } = getUnitConformationAndRadius(unit)
return Task.create('Molecular Surface', async ctx => {
return await MolecularSurface(ctx, position, unit.lookup3d.boundary.box, radius, props);
});
}
//
async function MolecularSurface(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: MolecularSurfaceCalculationProps): Promise<DensityData> {
return {
transform: Mat4,
field: Tensor,
idField: Tensor,
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment