diff --git a/src/mol-model/structure/model/properties/utils/accessible-surface-area.ts b/src/mol-model/structure/model/properties/utils/accessible-surface-area.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ab4c79a204b6956f6be4facf1aad27d70f6ec0b --- /dev/null +++ b/src/mol-model/structure/model/properties/utils/accessible-surface-area.ts @@ -0,0 +1,95 @@ +import { ResidueIndex } from 'mol-model/structure'; +import { MoleculeType } from 'mol-model/structure/model/types'; +import { GridLookup3D } from 'mol-math/geometry'; +import { Vec3 } from 'mol-math/linear-algebra'; +import { SortedArray } from 'mol-data/int'; +import { ElementIndex } from 'mol-model/structure/model/indexing'; +import { AtomicHierarchy, AtomicConformation } from '../atomic'; + +export function computeModelASA(hierarchy: AtomicHierarchy, conformation: AtomicConformation) { + const { lookup3d, proteinResidues } = calcAtomicTraceLookup3D(hierarchy, conformation) + const backboneIndices = calcBackboneAtomIndices(hierarchy, proteinResidues) + + const residueCount = proteinResidues.length + const numberOfSpherePoints = 960 + + const ctx: ASAContext = { + probeSize: 1.4, + spherePoints: generateSpherePoints(numberOfSpherePoints), + cons: 4.0 * Math.PI / numberOfSpherePoints, + + hierarchy, + proteinResidues + } + + calculateASA(ctx) +} + +function calculateASA(ctx: ASAContext) { + +} + +function generateSpherePoints(numberOfSpherePoints: number): Vec3[] { + const points: Vec3[] = [] + const inc = Math.PI + + return points +} + +interface ASAContext { + probeSize: number, + spherePoints: Vec3[], + cons: number, + + hierarchy: AtomicHierarchy, + proteinResidues: SortedArray<ResidueIndex> +} + +interface BackboneAtomIndices { + cIndices: ArrayLike<ElementIndex | -1> + hIndices: ArrayLike<ElementIndex | -1> + oIndices: ArrayLike<ElementIndex | -1> + nIndices: ArrayLike<ElementIndex | -1> +} + +function calcAtomicTraceLookup3D(hierarchy: AtomicHierarchy, conformation: AtomicConformation) { + const { x, y, z } = conformation; + const { moleculeType, traceElementIndex } = hierarchy.derived.residue + const indices: number[] = [] + const _proteinResidues: number[] = [] + for (let i = 0, il = moleculeType.length; i < il; ++i) { + if (moleculeType[i] === MoleculeType.protein) { + indices[indices.length] = traceElementIndex[i] + _proteinResidues[_proteinResidues.length] = i + } + } + const lookup3d = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(indices) }, 4); + const proteinResidues = SortedArray.ofSortedArray<ResidueIndex>(_proteinResidues) + return { lookup3d, proteinResidues } +} + + +function calcBackboneAtomIndices(hierarchy: AtomicHierarchy, proteinResidues: SortedArray<ResidueIndex>): BackboneAtomIndices { + const residueCount = proteinResidues.length + const { index } = hierarchy + + const c = new Int32Array(residueCount) + const h = new Int32Array(residueCount) + const o = new Int32Array(residueCount) + const n = new Int32Array(residueCount) + + for (let i = 0, il = residueCount; i < il; ++i) { + const rI = proteinResidues[i] + c[i] = index.findAtomOnResidue(rI, 'C') + h[i] = index.findAtomOnResidue(rI, 'H') + o[i] = index.findAtomOnResidue(rI, 'O') + n[i] = index.findAtomOnResidue(rI, 'N') + } + + return { + cIndices: c as unknown as ArrayLike<ElementIndex | -1>, + hIndices: h as unknown as ArrayLike<ElementIndex | -1>, + oIndices: o as unknown as ArrayLike<ElementIndex | -1>, + nIndices: n as unknown as ArrayLike<ElementIndex | -1>, + } +} \ No newline at end of file diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts index cab7d59fcd15c3839d33722eed8d6d929908f7ec..48eb75cc2eb2c2b5ea76e999d044cfc283c0f66e 100644 --- a/src/mol-theme/color.ts +++ b/src/mol-theme/color.ts @@ -7,6 +7,7 @@ import { Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { ColorType } from 'mol-geo/geometry/color-data'; +import { AccessibleSurfaceAreaColorThemeProvider } from './color/accessible-surface-area' import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbol'; import { UniformColorThemeProvider } from './color/uniform'; import { deepEqual } from 'mol-util'; @@ -62,6 +63,7 @@ namespace ColorTheme { } export const BuiltInColorThemes = { + 'accessible-surface-area': AccessibleSurfaceAreaColorThemeProvider, 'carbohydrate-symbol': CarbohydrateSymbolColorThemeProvider, 'chain-id': ChainIdColorThemeProvider, 'cross-link': CrossLinkColorThemeProvider, diff --git a/src/mol-theme/color/accessible-surface-area.ts b/src/mol-theme/color/accessible-surface-area.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f7307023902ef5291a876f1ba42862118e4204f --- /dev/null +++ b/src/mol-theme/color/accessible-surface-area.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Color } from 'mol-util/color'; +import { Location } from 'mol-model/location'; +import { ColorTheme } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from '../theme'; +import { ColorListOptions, ColorListName, ColorScale } from 'mol-util/color/scale'; + +// const DefaultAccessibleSurfaceAreaColor = Color(0xCCCCCC) +const Description = 'Assigns a color based on the relative accessible surface area of a residue.' + +export const AccessibleSurfaceAreaColorThemeParams = { + list: PD.ColorScale<ColorListName>('Rainbow', ColorListOptions), +} +export type AccessibleSurfaceAreaColorThemeParams = typeof AccessibleSurfaceAreaColorThemeParams +export function getAccessibleSurfaceAreaColorThemeParams(ctx: ThemeDataContext) { + return AccessibleSurfaceAreaColorThemeParams // TODO return copy +} + +export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD.Values<AccessibleSurfaceAreaColorThemeParams>): ColorTheme<AccessibleSurfaceAreaColorThemeParams> { + const scale = ColorScale.create({ + listOrName: props.list, + minLabel: 'Start', + maxLabel: 'End', + }) + const color = (location: Location): Color => { + return scale.color(Math.random()) + // TODO impl + } + + return { + factory: AccessibleSurfaceAreaColorTheme, + granularity: 'group', + color, + props, + description: Description, + legend: scale ? scale.legend : undefined + } +} + +export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams> = { + label: 'Accessible Surface Area', + factory: AccessibleSurfaceAreaColorTheme, + getParams: getAccessibleSurfaceAreaColorThemeParams, + defaultValues: PD.getDefaultValues(AccessibleSurfaceAreaColorThemeParams), + isApplicable: (ctx: ThemeDataContext) => !!ctx.structure +} \ No newline at end of file diff --git a/src/tests/browser/render-structure.ts b/src/tests/browser/render-structure.ts index 9dae8a78c7380888fce1cf4db1a64259745cba21..e16c54bf05a6bade3cf0784d640f3d3e7ad83060 100644 --- a/src/tests/browser/render-structure.ts +++ b/src/tests/browser/render-structure.ts @@ -12,7 +12,7 @@ import { ColorTheme } from 'mol-theme/color'; import { SizeTheme } from 'mol-theme/size'; import { CartoonRepresentationProvider } from 'mol-repr/structure/representation/cartoon'; import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif'; -import { computeModelDSSP } from 'mol-model/structure/model/properties/utils/secondary-structure'; +import { computeModelASA } from 'mol-model/structure/model/properties/utils/accessible-surface-area'; const parent = document.getElementById('app')! parent.style.width = '100%' @@ -62,21 +62,21 @@ function getCartoonRepr() { } async function init() { - const cif = await downloadFromPdb('3j3q') + const cif = await downloadFromPdb('1acj') const models = await getModels(cif) - console.time('computeModelDSSP') - const secondaryStructure = computeModelDSSP(models[0].atomicHierarchy, models[0].atomicConformation) - console.timeEnd('computeModelDSSP') - ;(models[0].properties as any).secondaryStructure = secondaryStructure + console.time('computeModelASA') + const asa = computeModelASA(models[0].atomicHierarchy, models[0].atomicConformation) + console.timeEnd('computeModelASA'); + (models[0].properties as any).asa = asa const structure = await getStructure(models[0]) const cartoonRepr = getCartoonRepr() cartoonRepr.setTheme({ - color: reprCtx.colorThemeRegistry.create('secondary-structure', { structure }), + color: reprCtx.colorThemeRegistry.create('accessible-surface-area', { structure }), size: reprCtx.sizeThemeRegistry.create('uniform', { structure }) }) await cartoonRepr.createOrUpdate({ ...CartoonRepresentationProvider.defaultValues, quality: 'auto' }, structure).run() - + canvas3d.add(cartoonRepr) canvas3d.resetCamera() }